--- /dev/null
+#include "config.h"
+
+#include "gskgldriverprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskprofilerprivate.h"
+#include "gdk/gdktextureprivate.h"
+
+#include <gdk/gdk.h>
+#include <epoxy/gl.h>
+
+typedef struct {
+ GLuint texture_id;
+ int width;
+ int height;
+ GLuint min_filter;
+ GLuint mag_filter;
+ GArray *fbos;
+ GdkTexture *user;
+ guint in_use : 1;
+ guint permanent : 1;
+} Texture;
+
+typedef struct {
+ GLuint vao_id;
+ GLuint buffer_id;
+ GLuint position_id;
+ GLuint uv_id;
+ GskQuadVertex *quads;
+ int n_quads;
+ guint in_use : 1;
+ guint permanent : 1;
+} Vao;
+
+typedef struct {
+ GLuint fbo_id;
+ GLuint depth_stencil_id;
+} Fbo;
+
+struct _GskGLDriver
+{
+ GObject parent_instance;
+
+ GdkGLContext *gl_context;
+ GskProfiler *profiler;
+ struct {
+ GQuark created_textures;
+ GQuark reused_textures;
+ GQuark surface_uploads;
+ } counters;
+
+ Fbo default_fbo;
+
+ GHashTable *textures;
+ GHashTable *vaos;
+
+ const Texture *bound_source_texture;
+ const Texture *bound_mask_texture;
+ const Vao *bound_vao;
+ const Fbo *bound_fbo;
+
+ int max_texture_size;
+
+ gboolean in_frame : 1;
+};
+
+enum
+{
+ PROP_GL_CONTEXT = 1,
+
+ N_PROPS
+};
+
+static GParamSpec *gsk_gl_driver_properties[N_PROPS];
+
+G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
+
+static Texture *
+texture_new (void)
+{
+ return g_slice_new0 (Texture);
+}
+
+static void
+texture_free (gpointer data)
+{
+ Texture *t = data;
+
+ if (t->user)
+ gdk_texture_clear_render_data (t->user);
+
+ g_clear_pointer (&t->fbos, g_array_unref);
+ glDeleteTextures (1, &t->texture_id);
+ g_slice_free (Texture, t);
+}
+
+static void
+fbo_clear (gpointer data)
+{
+ Fbo *f = data;
+
+ if (f->depth_stencil_id != 0)
+ glDeleteRenderbuffers (1, &f->depth_stencil_id);
+
+ glDeleteFramebuffers (1, &f->fbo_id);
+}
+
+static Vao *
+vao_new (void)
+{
+ return g_slice_new0 (Vao);
+}
+
+static void
+vao_free (gpointer data)
+{
+ Vao *v = data;
+
+ g_free (v->quads);
+ glDeleteBuffers (1, &v->buffer_id);
+ glDeleteVertexArrays (1, &v->vao_id);
+ g_slice_free (Vao, v);
+}
+
+static void
+gsk_gl_driver_finalize (GObject *gobject)
+{
+ GskGLDriver *self = GSK_GL_DRIVER (gobject);
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ g_clear_pointer (&self->textures, g_hash_table_unref);
+ g_clear_pointer (&self->vaos, g_hash_table_unref);
+ g_clear_object (&self->profiler);
+
+ if (self->gl_context == gdk_gl_context_get_current ())
+ gdk_gl_context_clear_current ();
+
+ g_clear_object (&self->gl_context);
+
+ G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_gl_driver_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLDriver *self = GSK_GL_DRIVER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GL_CONTEXT:
+ self->gl_context = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_driver_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLDriver *self = GSK_GL_DRIVER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GL_CONTEXT:
+ g_value_set_object (value, self->gl_context);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_driver_class_init (GskGLDriverClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gsk_gl_driver_set_property;
+ gobject_class->get_property = gsk_gl_driver_get_property;
+ gobject_class->finalize = gsk_gl_driver_finalize;
+
+ gsk_gl_driver_properties[PROP_GL_CONTEXT] =
+ g_param_spec_object ("gl-context", "GL Context", "The GL context used by the driver",
+ GDK_TYPE_GL_CONTEXT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, gsk_gl_driver_properties);
+}
+
+static void
+gsk_gl_driver_init (GskGLDriver *self)
+{
+ self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free);
+ self->vaos = g_hash_table_new_full (NULL, NULL, NULL, vao_free);
+
+ self->max_texture_size = -1;
+
+#ifdef G_ENABLE_DEBUG
+ self->profiler = gsk_profiler_new ();
+ self->counters.created_textures = gsk_profiler_add_counter (self->profiler,
+ "created_textures",
+ "Textures created this frame",
+ TRUE);
+ self->counters.reused_textures = gsk_profiler_add_counter (self->profiler,
+ "reused_textures",
+ "Textures reused this frame",
+ TRUE);
+ self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler,
+ "surface_uploads",
+ "Texture uploads from surfaces this frame",
+ TRUE);
+#endif
+}
+
+GskGLDriver *
+gsk_gl_driver_new (GdkGLContext *context)
+{
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return g_object_new (GSK_TYPE_GL_DRIVER,
+ "gl-context", context,
+ NULL);
+}
+
+void
+gsk_gl_driver_begin_frame (GskGLDriver *self)
+{
+ g_return_if_fail (GSK_IS_GL_DRIVER (self));
+ g_return_if_fail (!self->in_frame);
+
+ self->in_frame = TRUE;
+
+ if (self->max_texture_size < 0)
+ {
+ glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size);
+ GSK_NOTE (OPENGL, g_print ("GL max texture size: %d\n", self->max_texture_size));
+ }
+
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+ self->bound_fbo = &self->default_fbo;
+
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, 0);
+
+ glActiveTexture (GL_TEXTURE0 + 1);
+ glBindTexture (GL_TEXTURE_2D, 0);
+
+ glBindVertexArray (0);
+ glUseProgram (0);
+
+ glActiveTexture (GL_TEXTURE0);
+
+ gsk_profiler_reset (self->profiler);
+}
+
+void
+gsk_gl_driver_end_frame (GskGLDriver *self)
+{
+ g_return_if_fail (GSK_IS_GL_DRIVER (self));
+ g_return_if_fail (self->in_frame);
+
+ self->bound_source_texture = NULL;
+ self->bound_mask_texture = NULL;
+ self->bound_vao = NULL;
+ self->bound_fbo = NULL;
+
+ self->default_fbo.fbo_id = 0;
+
+ GSK_NOTE (OPENGL,
+ g_print ("Textures created: %ld\n"
+ " Textures reused: %ld\n"
+ " Surface uploads: %ld\n",
+ gsk_profiler_counter_get (self->profiler, self->counters.created_textures),
+ gsk_profiler_counter_get (self->profiler, self->counters.reused_textures),
+ gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads)));
+ GSK_NOTE (OPENGL,
+ g_print ("*** Frame end: textures=%d, vaos=%d\n",
+ g_hash_table_size (self->textures),
+ g_hash_table_size (self->vaos)));
+
+ self->in_frame = FALSE;
+}
+
+int
+gsk_gl_driver_collect_textures (GskGLDriver *driver)
+{
+ GHashTableIter iter;
+ gpointer value_p = NULL;
+ int old_size;
+ /*return;*/
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), 0);
+ g_return_val_if_fail (!driver->in_frame, 0);
+
+ old_size = g_hash_table_size (driver->textures);
+
+ g_hash_table_iter_init (&iter, driver->textures);
+ while (g_hash_table_iter_next (&iter, NULL, &value_p))
+ {
+ Texture *t = value_p;
+
+ if (t->user || t->permanent)
+ continue;
+
+ if (t->in_use)
+ {
+ t->in_use = FALSE;
+ g_clear_pointer (&t->fbos, g_array_unref);
+ }
+ else
+ g_hash_table_iter_remove (&iter);
+ }
+
+ return old_size - g_hash_table_size (driver->textures);
+}
+
+int
+gsk_gl_driver_collect_vaos (GskGLDriver *driver)
+{
+ GHashTableIter iter;
+ gpointer value_p = NULL;
+ int old_size;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), 0);
+ g_return_val_if_fail (!driver->in_frame, 0);
+
+ old_size = g_hash_table_size (driver->vaos);
+
+ g_hash_table_iter_init (&iter, driver->vaos);
+ while (g_hash_table_iter_next (&iter, NULL, &value_p))
+ {
+ Vao *v = value_p;
+
+ if (v->in_use)
+ v->in_use = FALSE;
+ else
+ g_hash_table_iter_remove (&iter);
+ }
+
+ return old_size - g_hash_table_size (driver->vaos);
+}
+
+int
+gsk_gl_driver_get_max_texture_size (GskGLDriver *driver)
+{
+ if (driver->max_texture_size < 0)
+ {
+ if (gdk_gl_context_get_use_es (driver->gl_context))
+ return 2048;
+
+ return 1024;
+ }
+
+ return driver->max_texture_size;
+}
+
+static Texture *
+gsk_gl_driver_get_texture (GskGLDriver *driver,
+ int texture_id)
+{
+ Texture *t;
+
+ if (g_hash_table_lookup_extended (driver->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t))
+ return t;
+
+ return NULL;
+}
+
+static Vao *
+gsk_gl_driver_get_vao (GskGLDriver *driver,
+ int vao_id)
+{
+ Vao *v;
+
+ if (g_hash_table_lookup_extended (driver->vaos, GINT_TO_POINTER (vao_id), NULL, (gpointer *) &v))
+ return v;
+
+ return NULL;
+}
+
+static Fbo *
+gsk_gl_driver_get_fbo (GskGLDriver *driver,
+ int texture_id)
+{
+ Texture *t = gsk_gl_driver_get_texture (driver, texture_id);
+
+ if (t->fbos == NULL)
+ return &driver->default_fbo;
+
+ return &g_array_index (t->fbos, Fbo, 0);
+}
+
+static Texture *
+find_texture_by_size (GHashTable *textures,
+ int width,
+ int height)
+{
+ GHashTableIter iter;
+ gpointer value_p = NULL;
+
+ g_hash_table_iter_init (&iter, textures);
+ while (g_hash_table_iter_next (&iter, NULL, &value_p))
+ {
+ Texture *t = value_p;
+
+ if (t->width == width && t->height == height)
+ return t;
+ }
+
+ return NULL;
+}
+
+static Texture *
+create_texture (GskGLDriver *self,
+ float fwidth,
+ float fheight)
+{
+ guint texture_id;
+ Texture *t;
+ int width = ceilf (fwidth);
+ int height = ceilf (fheight);
+
+ if (width >= self->max_texture_size ||
+ height >= self->max_texture_size)
+ {
+ g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
+ width, height,
+ self->max_texture_size);
+
+ width = MIN (width, self->max_texture_size);
+ height = MIN (height, self->max_texture_size);
+ }
+
+ t = find_texture_by_size (self->textures, width, height);
+ if (t != NULL && !t->in_use && t->user == NULL)
+ {
+ GSK_NOTE (OPENGL, g_print ("Reusing Texture(%d) for size %dx%d\n",
+ t->texture_id, t->width, t->height));
+ t->in_use = TRUE;
+ gsk_profiler_counter_inc (self->profiler, self->counters.reused_textures);
+ return t;
+ }
+
+ glGenTextures (1, &texture_id);
+
+ t = texture_new ();
+ t->texture_id = texture_id;
+ t->width = width;
+ t->height = height;
+ t->min_filter = GL_NEAREST;
+ t->mag_filter = GL_NEAREST;
+ t->in_use = TRUE;
+ g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t);
+ gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
+
+ return t;
+}
+
+static void
+gsk_gl_driver_release_texture (gpointer data)
+{
+ Texture *t = data;
+
+ t->user = NULL;
+}
+
+int
+gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
+ GdkTexture *texture,
+ int min_filter,
+ int mag_filter)
+{
+ Texture *t;
+ cairo_surface_t *surface;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
+ g_return_val_if_fail (GDK_IS_TEXTURE (texture), -1);
+
+ t = gdk_texture_get_render_data (texture, driver);
+
+ if (t)
+ {
+ if (t->min_filter == min_filter && t->mag_filter == mag_filter)
+ return t->texture_id;
+ }
+
+ t = create_texture (driver, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
+
+ if (gdk_texture_set_render_data (texture, driver, t, gsk_gl_driver_release_texture))
+ t->user = texture;
+
+ surface = gdk_texture_download_surface (texture);
+ gsk_gl_driver_bind_source_texture (driver, t->texture_id);
+ gsk_gl_driver_init_texture_with_surface (driver,
+ t->texture_id,
+ surface,
+ min_filter,
+ mag_filter);
+ cairo_surface_destroy (surface);
+
+ return t->texture_id;
+}
+
+int
+gsk_gl_driver_create_permanent_texture (GskGLDriver *self,
+ float width,
+ float height)
+{
+ Texture *t;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1);
+
+ t = create_texture (self, width, height);
+ t->permanent = TRUE;
+
+ return t->texture_id;
+}
+
+int
+gsk_gl_driver_create_texture (GskGLDriver *driver,
+ float width,
+ float height)
+{
+ Texture *t;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
+
+ t = create_texture (driver, width, height);
+
+ return t->texture_id;
+}
+
+static Vao *
+find_vao (GHashTable *vaos,
+ int position_id,
+ int uv_id,
+ int n_quads,
+ GskQuadVertex *quads)
+{
+ GHashTableIter iter;
+ gpointer value_p = NULL;
+
+ g_hash_table_iter_init (&iter, vaos);
+ while (g_hash_table_iter_next (&iter, NULL, &value_p))
+ {
+ Vao *v = value_p;
+
+ if (v->position_id != position_id || v->uv_id != uv_id)
+ continue;
+
+ if (v->n_quads != n_quads)
+ continue;
+
+ if (memcmp (v->quads, quads, sizeof (GskQuadVertex) * n_quads) == 0)
+ return v;
+ }
+
+ return NULL;
+}
+
+void
+gsk_gl_driver_create_permanent_vao_for_quad (GskGLDriver *driver,
+ int n_vertices,
+ const GskQuadVertex *quads,
+ int *out_vao_id,
+ int *out_vao_buffer_id)
+{
+ GLuint vao_id, buffer_id;
+
+ glGenVertexArrays (1, &vao_id);
+ glBindVertexArray (vao_id);
+
+ glGenBuffers (1, &buffer_id);
+ glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
+ glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_vertices, quads, GL_STATIC_DRAW);
+
+ glBindBuffer (GL_ARRAY_BUFFER, 0);
+ glBindVertexArray (0);
+
+ *out_vao_id = buffer_id;
+ *out_vao_buffer_id = vao_id;
+}
+
+int
+gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver,
+ int position_id,
+ int uv_id,
+ int n_vertices,
+ GskQuadVertex *quads)
+
+{
+ GLuint vao_id, buffer_id;
+ Vao *v;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
+ g_return_val_if_fail (driver->in_frame, -1);
+
+ v = find_vao (driver->vaos, position_id, uv_id, n_vertices, quads);
+ if (v != NULL && !v->in_use)
+ {
+ GSK_NOTE (OPENGL, g_print ("Reusing VAO(%d)\n", v->vao_id));
+ v->in_use = TRUE;
+ return v->vao_id;
+ }
+
+ glGenVertexArrays (1, &vao_id);
+ glBindVertexArray (vao_id);
+
+ glGenBuffers (1, &buffer_id);
+ glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
+ glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_vertices, quads, GL_STATIC_DRAW);
+
+ if (position_id != -1)
+ {
+ glEnableVertexAttribArray (position_id);
+ glVertexAttribPointer (position_id, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskQuadVertex),
+ (void *) G_STRUCT_OFFSET (GskQuadVertex, position));
+ }
+
+ if (uv_id != -1)
+ {
+ glEnableVertexAttribArray (uv_id);
+ glVertexAttribPointer (uv_id, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskQuadVertex),
+ (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
+ }
+
+ glBindBuffer (GL_ARRAY_BUFFER, 0);
+ glBindVertexArray (0);
+
+ v = vao_new ();
+ v->vao_id = vao_id;
+ v->buffer_id = buffer_id;
+ v->position_id = position_id;
+ v->uv_id = uv_id;
+ v->n_quads = n_vertices;
+ v->quads = g_memdup (quads, sizeof (GskQuadVertex) * n_vertices);
+ v->in_use = TRUE;
+ g_hash_table_insert (driver->vaos, GINT_TO_POINTER (vao_id), v);
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_DEBUG_CHECK (OPENGL))
+ {
+ int i;
+ g_print ("New VAO(%d) for quad[%d] : {\n", v->vao_id, n_vertices);
+ for (i = 0; i < n_vertices; i++)
+ {
+ g_print (" { x:%.2f, y:%.2f } { u:%.2f, v:%.2f }\n",
+ quads[i].position[0], quads[i].position[1],
+ quads[i].uv[0], quads[i].uv[1]);
+ }
+ g_print ("}\n");
+ }
+#endif
+
+ return vao_id;
+}
+
+int
+gsk_gl_driver_create_render_target (GskGLDriver *driver,
+ int texture_id,
+ gboolean add_depth_buffer,
+ gboolean add_stencil_buffer)
+{
+ GLuint fbo_id, depth_stencil_buffer_id;
+ Texture *t;
+ Fbo f;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
+ g_return_val_if_fail (driver->in_frame, -1);
+
+ t = gsk_gl_driver_get_texture (driver, texture_id);
+ if (t == NULL)
+ return -1;
+
+ if (t->fbos == NULL)
+ {
+ t->fbos = g_array_new (FALSE, FALSE, sizeof (Fbo));
+ g_array_set_clear_func (t->fbos, fbo_clear);
+ }
+
+ glGenFramebuffers (1, &fbo_id);
+ glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
+ glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, t->texture_id, 0);
+
+ if (add_depth_buffer || add_stencil_buffer)
+ glGenRenderbuffersEXT (1, &depth_stencil_buffer_id);
+ else
+ depth_stencil_buffer_id = 0;
+
+ glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id);
+
+ if (add_depth_buffer || add_stencil_buffer)
+ {
+ if (add_stencil_buffer)
+ glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height);
+ else
+ glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height);
+
+ if (add_depth_buffer)
+ glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_id);
+
+ if (add_stencil_buffer)
+ glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, depth_stencil_buffer_id);
+ }
+
+ f.fbo_id = fbo_id;
+ f.depth_stencil_id = depth_stencil_buffer_id;
+
+ g_array_append_val (t->fbos, f);
+
+ g_assert_cmpint (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
+ glBindFramebuffer (GL_FRAMEBUFFER, driver->default_fbo.fbo_id);
+
+ return fbo_id;
+}
+
+void
+gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
+ int texture_id)
+{
+ Texture *t;
+
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+ g_return_if_fail (driver->in_frame);
+
+ t = gsk_gl_driver_get_texture (driver, texture_id);
+ if (t == NULL)
+ {
+ g_critical ("No texture %d found.", texture_id);
+ return;
+ }
+
+ if (driver->bound_source_texture != t)
+ {
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, t->texture_id);
+
+ driver->bound_source_texture = t;
+ }
+}
+
+void
+gsk_gl_driver_bind_mask_texture (GskGLDriver *driver,
+ int texture_id)
+{
+ Texture *t;
+
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+ g_return_if_fail (driver->in_frame);
+
+ t = gsk_gl_driver_get_texture (driver, texture_id);
+ if (t == NULL)
+ {
+ g_critical ("No texture %d found.", texture_id);
+ return;
+ }
+
+ if (driver->bound_mask_texture != t)
+ {
+ glActiveTexture (GL_TEXTURE0 + 1);
+ glBindTexture (GL_TEXTURE_2D, t->texture_id);
+
+ glActiveTexture (GL_TEXTURE0);
+
+ driver->bound_mask_texture = t;
+ }
+}
+
+void
+gsk_gl_driver_bind_vao (GskGLDriver *driver,
+ int vao_id)
+{
+ Vao *v;
+
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+ g_return_if_fail (driver->in_frame);
+
+ v = gsk_gl_driver_get_vao (driver, vao_id);
+ if (v == NULL)
+ {
+ g_critical ("No VAO %d found.", vao_id);
+ return;
+ }
+
+ if (driver->bound_vao != v)
+ {
+ glBindVertexArray (v->vao_id);
+ glBindBuffer (GL_ARRAY_BUFFER, v->buffer_id);
+
+ if (v->position_id != -1)
+ glEnableVertexAttribArray (v->position_id);
+
+ if (v->uv_id != -1)
+ glEnableVertexAttribArray (v->uv_id);
+
+ driver->bound_vao = v;
+ }
+}
+
+gboolean
+gsk_gl_driver_bind_render_target (GskGLDriver *driver,
+ int texture_id)
+{
+ int status;
+ Fbo *f;
+
+ g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), FALSE);
+ g_return_val_if_fail (driver->in_frame, FALSE);
+
+ if (texture_id == 0)
+ {
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+ driver->bound_fbo = &driver->default_fbo;
+ goto out;
+ }
+
+ f = gsk_gl_driver_get_fbo (driver, texture_id);
+ if (f == NULL)
+ {
+ g_critical ("No render target associated to texture %d found.", texture_id);
+ return FALSE;
+ }
+
+ if (f != driver->bound_fbo)
+ {
+ glBindFramebuffer (GL_FRAMEBUFFER, f->fbo_id);
+
+ driver->bound_fbo = f;
+ }
+
+out:
+
+ if (texture_id != 0)
+ {
+ status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
+ g_assert_cmpint (status, ==, GL_FRAMEBUFFER_COMPLETE);
+ }
+
+ return TRUE;
+}
+
+void
+gsk_gl_driver_destroy_texture (GskGLDriver *driver,
+ int texture_id)
+{
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+
+ g_hash_table_remove (driver->textures, GINT_TO_POINTER (texture_id));
+}
+
+void
+gsk_gl_driver_destroy_vao (GskGLDriver *driver,
+ int vao_id)
+{
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+
+ g_hash_table_remove (driver->vaos, GINT_TO_POINTER (vao_id));
+}
+
+static void
+gsk_gl_driver_set_texture_parameters (GskGLDriver *driver,
+ int min_filter,
+ int mag_filter)
+{
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void
+gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
+ int texture_id)
+{
+ Texture *t;
+
+ g_return_if_fail (GSK_IS_GL_DRIVER (driver));
+
+ t = gsk_gl_driver_get_texture (driver, texture_id);
+ if (t == NULL)
+ {
+ g_critical ("No texture %d found.", texture_id);
+ return;
+ }
+
+ if (!(driver->bound_source_texture == t || driver->bound_mask_texture == t))
+ {
+ g_critical ("You must bind the texture before initializing it.");
+ return;
+ }
+
+ gsk_gl_driver_set_texture_parameters (driver, t->min_filter, t->mag_filter);
+
+ if (gdk_gl_context_get_use_es (driver->gl_context))
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ else
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glBindTexture (GL_TEXTURE_2D, 0);
+}
+
+void
+gsk_gl_driver_init_texture_with_surface (GskGLDriver *self,
+ int texture_id,
+ cairo_surface_t *surface,
+ int min_filter,
+ int mag_filter)
+{
+ Texture *t;
+
+ g_return_if_fail (GSK_IS_GL_DRIVER (self));
+
+ t = gsk_gl_driver_get_texture (self, texture_id);
+ if (t == NULL)
+ {
+ g_critical ("No texture %d found.", texture_id);
+ return;
+ }
+
+ if (!(self->bound_source_texture == t || self->bound_mask_texture == t))
+ {
+ g_critical ("You must bind the texture before initializing it.");
+ return;
+ }
+
+ gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
+
+ gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, t->width, t->height, NULL);
+ gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
+
+ t->min_filter = min_filter;
+ t->mag_filter = mag_filter;
+
+ if (t->min_filter != GL_NEAREST)
+ glGenerateMipmap (GL_TEXTURE_2D);
+}
--- /dev/null
+#ifndef __GSK_GL_DRIVER_PRIVATE_H__
+#define __GSK_GL_DRIVER_PRIVATE_H__
+
+#include <cairo.h>
+#include <gdk/gdk.h>
+#include <graphene.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type ())
+
+G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject)
+
+typedef struct {
+ float position[2];
+ float uv[2];
+} GskQuadVertex;
+
+GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
+
+int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
+
+void gsk_gl_driver_begin_frame (GskGLDriver *driver);
+void gsk_gl_driver_end_frame (GskGLDriver *driver);
+
+int gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
+ GdkTexture *texture,
+ int min_filter,
+ int mag_filter);
+int gsk_gl_driver_create_permanent_texture (GskGLDriver *driver,
+ float width,
+ float height);
+int gsk_gl_driver_create_texture (GskGLDriver *driver,
+ float width,
+ float height);
+int gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver,
+ int position_id,
+ int uv_id,
+ int n_vertices,
+ GskQuadVertex *vertices);
+void gsk_gl_driver_create_permanent_vao_for_quad (GskGLDriver *driver,
+ int n_vertices,
+ const GskQuadVertex *quads,
+ int *out_vao_id,
+ int *out_vao_buffer_id);
+int gsk_gl_driver_create_render_target (GskGLDriver *driver,
+ int texture_id,
+ gboolean add_depth_buffer,
+ gboolean add_stencil_buffer);
+
+void gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
+ int texture_id);
+void gsk_gl_driver_bind_mask_texture (GskGLDriver *driver,
+ int texture_id);
+void gsk_gl_driver_bind_vao (GskGLDriver *driver,
+ int vao_id);
+gboolean gsk_gl_driver_bind_render_target (GskGLDriver *driver,
+ int texture_id);
+
+void gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
+ int texture_id);
+void gsk_gl_driver_init_texture_with_surface (GskGLDriver *driver,
+ int texture_id,
+ cairo_surface_t *surface,
+ int min_filter,
+ int mag_filter);
+
+void gsk_gl_driver_destroy_texture (GskGLDriver *driver,
+ int texture_id);
+void gsk_gl_driver_destroy_vao (GskGLDriver *driver,
+ int vao_id);
+
+int gsk_gl_driver_collect_textures (GskGLDriver *driver);
+int gsk_gl_driver_collect_vaos (GskGLDriver *driver);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */
--- /dev/null
+#include "config.h"
+
+#include "gskglglyphcacheprivate.h"
+#include "gskgldriverprivate.h"
+#include "gskdebugprivate.h"
+#include "gskprivate.h"
+
+#include <graphene.h>
+#include <cairo/cairo.h>
+#include <epoxy/gl.h>
+
+/* Parameters for our cache eviction strategy.
+ *
+ * Each cached glyph has an age that gets reset every time a cached glyph gets used.
+ * Glyphs that have not been used for the MAX_AGE frames are considered old. We keep
+ * count of the pixels of each atlas that are taken up by old glyphs. We check the
+ * fraction of old pixels every CHECK_INTERVAL frames, and if it is above MAX_OLD, then
+ * we drop the atlas an all the glyphs contained in it from the cache.
+ */
+
+#define MAX_AGE 60
+#define CHECK_INTERVAL 10
+#define MAX_OLD 0.333
+
+#define ATLAS_SIZE 512
+
+typedef struct
+{
+ PangoFont *font;
+ PangoGlyph glyph;
+ guint scale; /* times 1024 */
+} GlyphCacheKey;
+
+typedef struct
+{
+ GlyphCacheKey *key;
+ GskGLCachedGlyph *value;
+ cairo_surface_t *surface;
+} DirtyGlyph;
+
+
+static guint glyph_cache_hash (gconstpointer v);
+static gboolean glyph_cache_equal (gconstpointer v1,
+ gconstpointer v2);
+static void glyph_cache_key_free (gpointer v);
+static void glyph_cache_value_free (gpointer v);
+static void dirty_glyph_free (gpointer v);
+
+static GskGLGlyphAtlas *
+create_atlas (GskGLGlyphCache *cache)
+{
+ GskGLGlyphAtlas *atlas;
+
+ atlas = g_new0 (GskGLGlyphAtlas, 1);
+ atlas->width = ATLAS_SIZE;
+ atlas->height = ATLAS_SIZE;
+ atlas->y0 = 1;
+ atlas->y = 1;
+ atlas->x = 1;
+ atlas->image = NULL;
+ atlas->num_glyphs = 0;
+ atlas->dirty_glyphs = NULL;
+
+ return atlas;
+}
+
+static void
+free_atlas (gpointer v)
+{
+ GskGLGlyphAtlas *atlas = v;
+
+ if (atlas->image)
+ {
+ g_assert (atlas->image->texture_id == 0);
+ g_free (atlas->image);
+ }
+ g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
+ g_free (atlas);
+}
+
+void
+gsk_gl_glyph_cache_init (GskGLGlyphCache *self,
+ GskGLDriver *gl_driver)
+{
+ self->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
+ glyph_cache_key_free, glyph_cache_value_free);
+ self->atlases = g_ptr_array_new_with_free_func (free_atlas);
+ g_ptr_array_add (self->atlases, create_atlas (self));
+
+ self->gl_driver = gl_driver;
+}
+
+void
+gsk_gl_glyph_cache_free (GskGLGlyphCache *self)
+{
+ guint i;
+
+ for (i = 0; i < self->atlases->len; i ++)
+ {
+ GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
+
+ if (atlas->image)
+ {
+ gsk_gl_image_destroy (atlas->image, self->gl_driver);
+ atlas->image->texture_id = 0;
+ }
+ }
+
+ g_ptr_array_unref (self->atlases);
+ g_hash_table_unref (self->hash_table);
+}
+
+static gboolean
+glyph_cache_equal (gconstpointer v1, gconstpointer v2)
+{
+ const GlyphCacheKey *key1 = v1;
+ const GlyphCacheKey *key2 = v2;
+
+ return key1->font == key2->font &&
+ key1->glyph == key2->glyph &&
+ key1->scale == key2->scale;
+}
+
+static guint
+glyph_cache_hash (gconstpointer v)
+{
+ const GlyphCacheKey *key = v;
+
+ return GPOINTER_TO_UINT (key->font) ^ key->glyph ^ key->scale;
+}
+
+static void
+glyph_cache_key_free (gpointer v)
+{
+ GlyphCacheKey *f = v;
+
+ g_object_unref (f->font);
+ g_free (f);
+}
+
+static void
+glyph_cache_value_free (gpointer v)
+{
+ g_free (v);
+}
+
+static void
+dirty_glyph_free (gpointer v)
+{
+ DirtyGlyph *glyph = v;
+
+ if (glyph->surface)
+ cairo_surface_destroy (glyph->surface);
+ g_free (glyph);
+}
+
+static void
+add_to_cache (GskGLGlyphCache *cache,
+ GlyphCacheKey *key,
+ GskGLCachedGlyph *value)
+{
+ GskGLGlyphAtlas *atlas;
+ int i;
+ DirtyGlyph *dirty;
+ int width = value->draw_width * key->scale / 1024;
+ int height = value->draw_height * key->scale / 1024;
+
+ for (i = 0; i < cache->atlases->len; i++)
+ {
+ int x, y, y0;
+
+ atlas = g_ptr_array_index (cache->atlases, i);
+ x = atlas->x;
+ y = atlas->y;
+ y0 = atlas->y0;
+
+ if (atlas->x + width + 1 >= atlas->width)
+ {
+ /* start a new row */
+ y0 = y + 1;
+ x = 1;
+ }
+
+ if (y0 + height + 1 >= atlas->height)
+ continue;
+
+ atlas->y0 = y0;
+ atlas->x = x;
+ atlas->y = y;
+ break;
+ }
+
+ if (i == cache->atlases->len)
+ {
+ atlas = create_atlas (cache);
+ g_ptr_array_add (cache->atlases, atlas);
+ }
+
+ value->tx = (float)atlas->x / atlas->width;
+ value->ty = (float)atlas->y0 / atlas->height;
+ value->tw = (float)width / atlas->width;
+ value->th = (float)height / atlas->height;
+
+ value->atlas = atlas;
+
+ dirty = g_new0 (DirtyGlyph, 1);
+ dirty->key = key;
+ dirty->value = value;
+ atlas->dirty_glyphs = g_list_prepend (atlas->dirty_glyphs, dirty);
+
+ atlas->x = atlas->x + width + 1;
+ atlas->y = MAX (atlas->y, atlas->y0 + height + 1);
+
+ atlas->num_glyphs++;
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_DEBUG_CHECK(GLYPH_CACHE))
+ {
+ g_print ("Glyph cache:\n");
+ for (i = 0; i < cache->atlases->len; i++)
+ {
+ atlas = g_ptr_array_index (cache->atlases, i);
+ g_print ("\tGskGLGlyphAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
+ i, atlas->width, atlas->height,
+ atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
+ 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
+ atlas->x, atlas->y0, atlas->y);
+ }
+ }
+#endif
+}
+
+static void
+render_glyph (const GskGLGlyphAtlas *atlas,
+ DirtyGlyph *glyph,
+ GskImageRegion *region)
+{
+ GlyphCacheKey *key = glyph->key;
+ GskGLCachedGlyph *value = glyph->value;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_scaled_font_t *scaled_font;
+ cairo_glyph_t cg;
+
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->font);
+ if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
+ return;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ value->draw_width * key->scale / 1024,
+ value->draw_height * key->scale / 1024);
+ cairo_surface_set_device_scale (surface, key->scale / 1024.0, key->scale / 1024.0);
+
+ cr = cairo_create (surface);
+
+ cairo_set_scaled_font (cr, scaled_font);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+
+ cg.index = key->glyph;
+ cg.x = - value->draw_x;
+ cg.y = - value->draw_y;
+
+ cairo_show_glyphs (cr, &cg, 1);
+
+ cairo_destroy (cr);
+
+ glyph->surface = surface;
+
+ region->data = cairo_image_surface_get_data (surface);
+ region->width = cairo_image_surface_get_width (surface);
+ region->height = cairo_image_surface_get_height (surface);
+ region->stride = cairo_image_surface_get_stride (surface);
+ region->x = (gsize)(value->tx * atlas->width);
+ region->y = (gsize)(value->ty * atlas->height);
+}
+
+static void
+upload_dirty_glyphs (GskGLGlyphCache *self,
+ GskGLGlyphAtlas *atlas)
+{
+ GList *l;
+ guint num_regions;
+ GskImageRegion *regions;
+ int i;
+
+ num_regions = g_list_length (atlas->dirty_glyphs);
+ regions = alloca (sizeof (GskImageRegion) * num_regions);
+
+ for (l = atlas->dirty_glyphs, i = 0; l; l = l->next, i++)
+ render_glyph (atlas, (DirtyGlyph *)l->data, ®ions[i]);
+
+ GSK_NOTE (GLYPH_CACHE,
+ g_print ("uploading %d glyphs to cache\n", num_regions));
+
+
+ gsk_gl_image_upload_regions (atlas->image, self->gl_driver, num_regions, regions);
+
+ g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
+ atlas->dirty_glyphs = NULL;
+}
+
+const GskGLCachedGlyph *
+gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
+ gboolean create,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale)
+{
+ GlyphCacheKey lookup_key;
+ GskGLCachedGlyph *value;
+
+ lookup_key.font = font;
+ lookup_key.glyph = glyph;
+ lookup_key.scale = (guint)(scale * 1024);
+
+ value = g_hash_table_lookup (cache->hash_table, &lookup_key);
+
+ if (value)
+ {
+ if (cache->timestamp - value->timestamp >= MAX_AGE)
+ {
+ GskGLGlyphAtlas *atlas = value->atlas;
+
+ if (atlas)
+ atlas->old_pixels -= value->draw_width * value->draw_height;
+
+ value->timestamp = cache->timestamp;
+ }
+ }
+
+ if (create && value == NULL)
+ {
+ GlyphCacheKey *key;
+ PangoRectangle ink_rect;
+
+ key = g_new0 (GlyphCacheKey, 1);
+ value = g_new0 (GskGLCachedGlyph, 1);
+
+ pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
+ pango_extents_to_pixels (&ink_rect, NULL);
+
+ value->draw_x = ink_rect.x;
+ value->draw_y = ink_rect.y;
+ value->draw_width = ink_rect.width;
+ value->draw_height = ink_rect.height;
+ value->timestamp = cache->timestamp;
+ value->atlas = NULL; /* For now */
+
+ key->font = g_object_ref (font);
+ key->glyph = glyph;
+ key->scale = (guint)(scale * 1024);
+
+ if (ink_rect.width > 0 && ink_rect.height > 0)
+ add_to_cache (cache, key, value);
+
+ g_hash_table_insert (cache->hash_table, key, value);
+ }
+
+ return value;
+}
+
+GskGLImage *
+gsk_gl_glyph_cache_get_glyph_image (GskGLGlyphCache *self,
+ const GskGLCachedGlyph *glyph)
+{
+ GskGLGlyphAtlas *atlas = glyph->atlas;
+
+ g_assert (atlas != NULL);
+
+
+ if (atlas->image == NULL)
+ {
+ atlas->image = g_new0 (GskGLImage, 1);
+ gsk_gl_image_create (atlas->image, self->gl_driver, atlas->width, atlas->height);
+ }
+
+ if (atlas->dirty_glyphs)
+ upload_dirty_glyphs (self, atlas);
+
+ return atlas->image;
+}
+
+void
+gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
+{
+ int i;
+ GHashTableIter iter;
+ GlyphCacheKey *key;
+ GskGLCachedGlyph *value;
+ guint dropped = 0;
+
+ self->timestamp++;
+
+
+ if (self->timestamp % CHECK_INTERVAL != 0)
+ return;
+
+ /* look for glyphs that have grown old since last time */
+ g_hash_table_iter_init (&iter, self->hash_table);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ guint age;
+
+ age = self->timestamp - value->timestamp;
+ if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
+ {
+ GskGLGlyphAtlas *atlas = value->atlas;
+
+ if (atlas)
+ atlas->old_pixels += value->draw_width * value->draw_height;
+ }
+ }
+
+ /* look for atlases to drop, and create a mapping of updated texture indices */
+ for (i = self->atlases->len - 1; i >= 0; i--)
+ {
+ GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
+
+ if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
+ {
+ GSK_NOTE(GLYPH_CACHE,
+ g_print ("Dropping atlas %d (%g.2%% old)\n",
+ i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height)));
+
+ if (atlas->image)
+ {
+ gsk_gl_image_destroy (atlas->image, self->gl_driver);
+ atlas->image->texture_id = 0;
+ }
+
+ /* Remove all glyphs that point to this atlas */
+ g_hash_table_iter_init (&iter, self->hash_table);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+ {
+ if (value->atlas == atlas)
+ g_hash_table_iter_remove (&iter);
+ }
+ /* TODO: The above loop inside this other loop could be slow... */
+
+ g_ptr_array_remove_index (self->atlases, i);
+ }
+ }
+
+ GSK_NOTE(GLYPH_CACHE, g_print ("Dropped %d glyphs\n", dropped));
+}
--- /dev/null
+#ifndef __GSK_GL_GLYPH_CACHE_PRIVATE_H__
+#define __GSK_GL_GLYPH_CACHE_PRIVATE_H__
+
+#include "gskgldriverprivate.h"
+#include "gskglimageprivate.h"
+#include <pango/pango.h>
+#include <gdk/gdk.h>
+
+typedef struct
+{
+ GskGLDriver *gl_driver;
+
+ GHashTable *hash_table;
+ GPtrArray *atlases;
+
+ guint64 timestamp;
+} GskGLGlyphCache;
+
+
+typedef struct
+{
+ GskGLImage *image;
+ int width, height;
+ int x, y, y0;
+ int num_glyphs;
+ GList *dirty_glyphs;
+ guint old_pixels;
+} GskGLGlyphAtlas;
+
+typedef struct
+{
+ GskGLGlyphAtlas *atlas;
+
+ float tx;
+ float ty;
+ float tw;
+ float th;
+
+ int draw_x;
+ int draw_y;
+ int draw_width;
+ int draw_height;
+
+ guint64 timestamp;
+} GskGLCachedGlyph;
+
+void gsk_gl_glyph_cache_init (GskGLGlyphCache *self,
+ GskGLDriver *gl_driver);
+void gsk_gl_glyph_cache_free (GskGLGlyphCache *self);
+void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self);
+GskGLImage * gsk_gl_glyph_cache_get_glyph_image (GskGLGlyphCache *self,
+ const GskGLCachedGlyph *glyph);
+const GskGLCachedGlyph * gsk_gl_glyph_cache_lookup (GskGLGlyphCache *self,
+ gboolean create,
+ PangoFont *font,
+ PangoGlyph glyph,
+ float scale);
+
+#endif
--- /dev/null
+
+#include "gskglimageprivate.h"
+#include <epoxy/gl.h>
+
+void
+gsk_gl_image_create (GskGLImage *self,
+ GskGLDriver *gl_driver,
+ int width,
+ int height)
+{
+ self->texture_id = gsk_gl_driver_create_permanent_texture (gl_driver, width, height);
+ self->width = width;
+ self->height = height;
+
+ gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
+ gsk_gl_driver_init_texture_empty (gl_driver, self->texture_id);
+}
+
+void
+gsk_gl_image_destroy (GskGLImage *self,
+ GskGLDriver *gl_driver)
+{
+ gsk_gl_driver_destroy_texture (gl_driver, self->texture_id);
+}
+
+void
+gsk_gl_image_write_to_png (const GskGLImage *self,
+ GskGLDriver *gl_driver,
+ const char *filename)
+{
+ int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, self->width);
+ guchar *data = g_malloc (self->height * stride);
+ cairo_surface_t *s;
+
+ gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
+ glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+ s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, self->width, self->height, stride);
+ cairo_surface_write_to_png (s, filename);
+
+ cairo_surface_destroy (s);
+ g_free (data);
+}
+
+void
+gsk_gl_image_upload_regions (GskGLImage *self,
+ GskGLDriver *gl_driver,
+ guint n_regions,
+ const GskImageRegion *regions)
+{
+ guint i;
+
+ for (i = 0; i < n_regions; i ++)
+ {
+ const GskImageRegion *region = ®ions[i];
+
+ gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
+ glBindTexture (GL_TEXTURE_2D, self->texture_id);
+
+ glTexSubImage2D (GL_TEXTURE_2D, 0, region->x, region->y, region->width, region->height,
+ GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data);
+ }
+
+#ifdef G_ENABLE_DEBUG
+ /*gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);*/
+ /*gsk_gl_image_dump (self, gl_driver, "/home/baedert/atlases/test_dump.png");*/
+#endif
+}
+
--- /dev/null
+#ifndef __GSK_GL_IMAGE_H__
+#define __GSK_GL_IMAGE_H__
+
+#include "gskgldriverprivate.h"
+#include <cairo/cairo.h>
+
+typedef struct
+{
+ guint texture_id;
+ int width;
+ int height;
+} GskGLImage;
+
+typedef struct
+{
+ guchar *data;
+ gsize width;
+ gsize height;
+ gsize stride;
+ gsize x;
+ gsize y;
+} GskImageRegion;
+
+void gsk_gl_image_create (GskGLImage *self,
+ GskGLDriver *gl_driver,
+ int width,
+ int height);
+void gsk_gl_image_destroy (GskGLImage *self,
+ GskGLDriver *gl_driver);
+void gsk_gl_image_write_to_png (const GskGLImage *self,
+ GskGLDriver *gl_driver,
+ const char *filename);
+void gsk_gl_image_upload_regions (GskGLImage *self,
+ GskGLDriver *gl_driver,
+ guint n_regions,
+ const GskImageRegion *regions);
+
+
+#endif
+
--- /dev/null
+#include "config.h"
+
+#include "gskglprofilerprivate.h"
+
+#include <epoxy/gl.h>
+
+#define N_QUERIES 4
+
+struct _GskGLProfiler
+{
+ GObject parent_instance;
+
+ GdkGLContext *gl_context;
+
+ /* Creating GL queries is kind of expensive, so we pay the
+ * price upfront and create a circular buffer of queries
+ */
+ GLuint gl_queries[N_QUERIES];
+ GLuint active_query;
+
+ gboolean has_timer : 1;
+ gboolean first_frame : 1;
+};
+
+enum {
+ PROP_GL_CONTEXT = 1,
+
+ N_PROPERTIES
+};
+
+static GParamSpec *gsk_gl_profiler_properties[N_PROPERTIES];
+
+G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT)
+
+static void
+gsk_gl_profiler_finalize (GObject *gobject)
+{
+ GskGLProfiler *self = GSK_GL_PROFILER (gobject);
+
+ glDeleteQueries (N_QUERIES, self->gl_queries);
+
+ g_clear_object (&self->gl_context);
+
+ G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_gl_profiler_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLProfiler *self = GSK_GL_PROFILER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GL_CONTEXT:
+ self->gl_context = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_profiler_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GskGLProfiler *self = GSK_GL_PROFILER (gobject);
+
+ switch (prop_id)
+ {
+ case PROP_GL_CONTEXT:
+ g_value_set_object (value, self->gl_context);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+ }
+}
+
+static void
+gsk_gl_profiler_class_init (GskGLProfilerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = gsk_gl_profiler_set_property;
+ gobject_class->get_property = gsk_gl_profiler_get_property;
+ gobject_class->finalize = gsk_gl_profiler_finalize;
+
+ gsk_gl_profiler_properties[PROP_GL_CONTEXT] =
+ g_param_spec_object ("gl-context",
+ "GL Context",
+ "The GdkGLContext used by the GL profiler",
+ GDK_TYPE_GL_CONTEXT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPERTIES, gsk_gl_profiler_properties);
+}
+
+static void
+gsk_gl_profiler_init (GskGLProfiler *self)
+{
+ glGenQueries (N_QUERIES, self->gl_queries);
+
+ self->first_frame = TRUE;
+ self->has_timer = epoxy_has_gl_extension ("GL_ARB_timer_query");
+}
+
+GskGLProfiler *
+gsk_gl_profiler_new (GdkGLContext *context)
+{
+ g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
+
+ return g_object_new (GSK_TYPE_GL_PROFILER, "gl-context", context, NULL);
+}
+
+void
+gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler)
+{
+ GLuint query_id;
+
+ g_return_if_fail (GSK_IS_GL_PROFILER (profiler));
+
+ if (!profiler->has_timer)
+ return;
+
+ query_id = profiler->gl_queries[profiler->active_query];
+ glBeginQuery (GL_TIME_ELAPSED, query_id);
+}
+
+guint64
+gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler)
+{
+ GLuint last_query_id;
+ GLint res;
+ GLuint64 elapsed;
+
+ g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0);
+
+ if (!profiler->has_timer)
+ return 0;
+
+ glEndQuery (GL_TIME_ELAPSED);
+
+ if (profiler->active_query == 0)
+ last_query_id = N_QUERIES - 1;
+ else
+ last_query_id = profiler->active_query - 1;
+
+ /* Advance iterator */
+ profiler->active_query += 1;
+ if (profiler->active_query == N_QUERIES)
+ profiler->active_query = 0;
+
+ /* If this is the first frame we already have a result */
+ if (profiler->first_frame)
+ {
+ profiler->first_frame = FALSE;
+ return 0;
+ }
+
+ glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res);
+ if (res == 1)
+ glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed);
+ else
+ elapsed = 0;
+
+ return elapsed;
+}
--- /dev/null
+#ifndef __GSK_GL_PROFILER_PRIVATE_H__
+#define __GSK_GL_PROFILER_PRIVATE_H__
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ())
+G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject)
+
+GskGLProfiler * gsk_gl_profiler_new (GdkGLContext *context);
+
+void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler);
+guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */
--- /dev/null
+#include "config.h"
+
+#include "gskglrendererprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskenums.h"
+#include "gskgldriverprivate.h"
+#include "gskglprofilerprivate.h"
+#include "gskprofilerprivate.h"
+#include "gskrendererprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gskshaderbuilderprivate.h"
+#include "gskglglyphcacheprivate.h"
+#include "gdk/gdktextureprivate.h"
+
+#include "gskprivate.h"
+
+#include <epoxy/gl.h>
+#include <cairo-ft.h>
+
+#define SHADER_VERSION_GLES 100
+#define SHADER_VERSION_GL2_LEGACY 110
+#define SHADER_VERSION_GL3_LEGACY 130
+#define SHADER_VERSION_GL3 150
+
+#define ORTHO_NEAR_PLANE -10000
+#define ORTHO_FAR_PLANE 10000
+
+#define N_VERTICES 6
+#define N_PROGRAMS 6
+
+#define HIGHLIGHT_FALLBACK 0
+#define DEBUG_OPS 0
+
+#if DEBUG_OPS
+#define OP_PRINT(format, ...) g_print(format, ## __VA_ARGS__)
+#else
+#define OP_PRINT(format, ...)
+#endif
+
+#define INIT_PROGRAM_UNIFORM_LOCATION(program_name, location_name, uniform_name) \
+ G_STMT_START{\
+ self->program_name.location_name = glGetUniformLocation(self->program_name.id, uniform_name);\
+ g_assert (self->program_name.location_name != 0); \
+ }G_STMT_END
+
+
+
+static void G_GNUC_UNUSED
+dump_framebuffer (const char *filename, int w, int h)
+{
+ int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w);
+ guchar *data = g_malloc (h * stride);
+ cairo_surface_t *s;
+
+ glReadPixels (0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, w, h, stride);
+ cairo_surface_write_to_png (s, filename);
+
+ cairo_surface_destroy (s);
+ g_free (data);
+}
+
+static gboolean G_GNUC_UNUSED
+font_has_color_glyphs (const PangoFont *font)
+{
+ cairo_scaled_font_t *scaled_font;
+ gboolean has_color = FALSE;
+
+ scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
+ {
+ FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
+ has_color = (FT_HAS_COLOR (ft_face) != 0);
+ cairo_ft_scaled_font_unlock_face (scaled_font);
+ }
+
+ return has_color;
+}
+
+static void
+gsk_gl_renderer_setup_render_mode (GskGLRenderer *self);
+
+typedef struct
+{
+ int index; /* Into the renderer's program array */
+ const char *name; /* For debugging */
+
+ int id;
+ /* Common locations (gl_common)*/
+ int source_location;
+ int mask_location;
+ int uv_location;
+ int position_location;
+ int alpha_location;
+ int blend_mode_location;
+ int viewport_location;
+ int projection_location;
+ int modelview_location;
+ int clip_location;
+ int clip_corner_widths_location;
+ int clip_corner_heights_location;
+
+ /* Program-specific locations */
+ union {
+ struct {
+ int color_location;
+ };
+ struct {
+ int color_matrix_location;
+ int color_offset_location;
+ };
+ struct {
+ int n_color_stops_location;
+ int color_stops_location;
+ int color_offsets_location;
+ int start_point_location;
+ int end_point_location;
+ };
+ struct {
+ int clip_bounds_location;
+ int corner_widths_location;
+ int corner_heights_location;
+ };
+ };
+} Program;
+
+enum {
+ MODE_BLIT = 1,
+ MODE_COLOR,
+ MODE_COLORING,
+ MODE_TEXTURE,
+ MODE_COLOR_MATRIX,
+ MODE_LINEAR_GRADIENT,
+ N_MODES
+};
+
+enum {
+ OP_NONE,
+ OP_CHANGE_OPACITY = 1,
+ OP_CHANGE_COLOR = 2,
+ OP_CHANGE_PROJECTION = 3,
+ OP_CHANGE_MODELVIEW = 4,
+ OP_CHANGE_PROGRAM = 5,
+ OP_CHANGE_RENDER_TARGET = 6,
+ OP_CHANGE_CLIP = 7,
+ OP_CHANGE_VIEWPORT = 8,
+ OP_CHANGE_SOURCE_TEXTURE = 9,
+ OP_CHANGE_VAO = 10,
+ OP_CHANGE_LINEAR_GRADIENT = 11,
+ OP_DRAW = 12,
+};
+
+typedef struct
+{
+ guint op;
+
+ union {
+ float opacity;
+ graphene_matrix_t modelview; // TODO: Make both matrix members just "matrix".
+ graphene_matrix_t projection;
+ const Program *program;
+ GdkRGBA color;
+ gsize vao_offset;
+ GskQuadVertex vertex_data[N_VERTICES]; // New Quad
+ int texture_id;
+ int render_target_id;
+ GskRoundedRect clip;
+ graphene_rect_t viewport;
+ struct {
+ int n_color_stops;
+ float color_offsets[8];
+ float color_stops[4 * 8];
+ graphene_point_t start_point;
+ graphene_point_t end_point;
+ } linear_gradient;
+ };
+} RenderOp;
+
+#ifdef G_ENABLE_DEBUG
+typedef struct
+{
+ GQuark frames;
+ GQuark draw_calls;
+} ProfileCounters;
+
+typedef struct
+{
+ GQuark cpu_time;
+ GQuark gpu_time;
+} ProfileTimers;
+#endif
+
+
+typedef enum
+{
+ RENDER_FULL,
+ RENDER_SCISSOR
+} RenderMode;
+
+struct _GskGLRenderer
+{
+ GskRenderer parent_instance;
+
+ int scale_factor;
+
+ graphene_rect_t viewport;
+
+ guint frame_buffer;
+ guint depth_stencil_buffer;
+ guint texture_id;
+
+
+ GdkGLContext *gl_context;
+ GskGLDriver *gl_driver;
+ GskGLProfiler *gl_profiler;
+
+ union {
+ Program programs[N_PROGRAMS];
+ struct {
+ Program blend_program;
+ Program blit_program;
+ Program color_program;
+ Program coloring_program;
+ Program color_matrix_program;
+ Program linear_gradient_program;
+ };
+ };
+
+ GArray *render_ops;
+
+ GskGLGlyphCache glyph_cache;
+ int full_vao_id;
+ int full_vao_buffer_id;
+
+#ifdef G_ENABLE_DEBUG
+ ProfileCounters profile_counters;
+ ProfileTimers profile_timers;
+#endif
+
+ RenderMode render_mode;
+
+ gboolean has_buffers : 1;
+};
+
+struct _GskGLRendererClass
+{
+ GskRendererClass parent_class;
+};
+
+G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
+
+typedef struct
+{
+ /* Per-Program State */
+ struct {
+ GskRoundedRect clip;
+ graphene_matrix_t modelview;
+ graphene_matrix_t projection;
+ int source_texture;
+ graphene_rect_t viewport;
+ } program_state[N_PROGRAMS];
+
+ /* Current global state */
+ const Program *current_program;
+ int current_render_target;
+ int current_vao;
+ int current_texture;
+ GskRoundedRect current_clip;
+ graphene_matrix_t current_modelview;
+ graphene_matrix_t current_projection;
+ graphene_rect_t current_viewport;
+ float current_opacity;
+
+ gsize buffer_size;
+
+ GskGLRenderer *renderer;
+} RenderOpBuilder;
+
+static void
+add_program_op (RenderOpBuilder *builder,
+ const Program *new_program)
+{
+ static const GskRoundedRect empty_clip;
+ static const graphene_matrix_t empty_matrix;
+ static const graphene_rect_t empty_rect;
+ RenderOp op;
+
+ if (builder->current_program == new_program)
+ return;
+
+ op.op = OP_CHANGE_PROGRAM;
+ op.program = new_program;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->current_program = new_program;
+
+ /* If the projection is not yet set for this program, we use the current one. */
+ if (memcmp (&empty_matrix, &builder->program_state[new_program->index].projection, sizeof (graphene_matrix_t)) == 0 ||
+ memcmp (&builder->current_projection, &builder->program_state[new_program->index].projection, sizeof (graphene_matrix_t)) != 0)
+ {
+ op.op = OP_CHANGE_PROJECTION;
+ op.projection = builder->current_projection;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->program_state[new_program->index].projection = builder->current_projection;
+ }
+
+ if (memcmp (&empty_matrix, &builder->program_state[new_program->index].modelview, sizeof (graphene_matrix_t)) == 0 ||
+ memcmp (&builder->current_modelview, &builder->program_state[new_program->index].modelview, sizeof (graphene_matrix_t)) != 0)
+ {
+ op.op = OP_CHANGE_MODELVIEW;
+ op.modelview = builder->current_modelview;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->program_state[new_program->index].modelview = builder->current_modelview;
+ }
+
+ if (memcmp (&empty_rect, &builder->program_state[new_program->index].viewport, sizeof (graphene_rect_t)) == 0 ||
+ memcmp (&builder->current_viewport, &builder->program_state[new_program->index].viewport, sizeof (graphene_rect_t)) != 0)
+ {
+ op.op = OP_CHANGE_VIEWPORT;
+ op.viewport = builder->current_viewport;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->program_state[new_program->index].viewport = builder->current_viewport;
+ }
+
+ if (memcmp (&empty_clip, &builder->program_state[new_program->index].clip, sizeof (GskRoundedRect)) == 0 ||
+ memcmp (&builder->current_clip, &builder->program_state[new_program->index].clip, sizeof (GskRoundedRect)) != 0)
+ {
+ op.op = OP_CHANGE_CLIP;
+ op.clip = builder->current_clip;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->program_state[new_program->index].clip = builder->current_clip;
+ }
+
+ if (graphene_rect_equal (&empty_rect, &builder->program_state[new_program->index].viewport) ||
+ !graphene_rect_equal (&builder->current_viewport, &builder->program_state[new_program->index].viewport))
+ {
+ op.op = OP_CHANGE_VIEWPORT;
+ op.viewport = builder->current_viewport;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->program_state[new_program->index].viewport = builder->current_viewport;
+ }
+}
+
+static GskRoundedRect
+add_clip_op (RenderOpBuilder *builder,
+ const GskRoundedRect *new_clip)
+{
+ RenderOp op;
+ GskRoundedRect prev_clip;
+
+ op.op = OP_CHANGE_CLIP;
+ op.clip = *new_clip;
+ g_array_append_val (builder->renderer->render_ops, op);
+
+ if (builder->current_program != NULL)
+ builder->program_state[builder->current_program->index].clip = *new_clip;
+
+ prev_clip = builder->current_clip;
+ builder->current_clip = *new_clip;
+
+ return prev_clip;
+}
+
+static graphene_matrix_t
+add_modelview_op (RenderOpBuilder *builder,
+ const graphene_matrix_t *matrix)
+{
+ RenderOp op;
+ graphene_matrix_t prev_mv;
+ RenderOp *last_op;
+
+ last_op = &g_array_index (builder->renderer->render_ops, RenderOp, builder->renderer->render_ops->len - 1);
+ if (last_op->op == OP_CHANGE_MODELVIEW)
+ {
+ last_op->modelview = *matrix;
+ }
+ else
+ {
+ op.op = OP_CHANGE_MODELVIEW;
+ op.modelview = *matrix;
+ g_array_append_val (builder->renderer->render_ops, op);
+ }
+
+ if (builder->current_program != NULL)
+ builder->program_state[builder->current_program->index].modelview = *matrix;
+
+ prev_mv = builder->current_modelview;
+ builder->current_modelview = *matrix;
+
+ return prev_mv;
+}
+
+static graphene_matrix_t
+add_projection_op (RenderOpBuilder *builder,
+ const graphene_matrix_t *matrix)
+{
+ RenderOp op;
+ graphene_matrix_t prev_proj;
+
+ op.op = OP_CHANGE_PROJECTION;
+ op.projection = *matrix;
+ g_array_append_val (builder->renderer->render_ops, op);
+
+ if (builder->current_program != NULL)
+ builder->program_state[builder->current_program->index].projection = *matrix;
+
+ prev_proj = builder->current_projection;
+ builder->current_projection = *matrix;
+
+ return prev_proj;
+}
+
+static graphene_rect_t
+add_viewport_op (RenderOpBuilder *builder,
+ const graphene_rect_t *viewport)
+{
+ RenderOp op;
+ graphene_rect_t prev_viewport;
+
+ op.op = OP_CHANGE_VIEWPORT;
+ op.viewport = *viewport;
+ g_array_append_val (builder->renderer->render_ops, op);
+
+ if (builder->current_program != NULL)
+ builder->program_state[builder->current_program->index].viewport = *viewport;
+
+ prev_viewport = builder->current_viewport;
+ builder->current_viewport = *viewport;
+
+ return prev_viewport;
+}
+
+static void
+add_texture_op (RenderOpBuilder *builder,
+ int texture_id)
+{
+ RenderOp op;
+
+ if (builder->current_texture == texture_id)
+ return;
+
+ op.op = OP_CHANGE_SOURCE_TEXTURE;
+ op.texture_id = texture_id;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->current_texture = texture_id;
+}
+
+static float
+add_opacity_op (RenderOpBuilder *builder,
+ float opacity)
+{
+ RenderOp op;
+ float prev_opacity;
+
+ if (builder->current_opacity == opacity)
+ return opacity;
+
+ op.op = OP_CHANGE_OPACITY;
+ op.opacity = opacity;
+ g_array_append_val (builder->renderer->render_ops, op);
+
+ prev_opacity = builder->current_opacity;
+ builder->current_opacity = opacity;
+
+ return prev_opacity;
+}
+
+static int
+add_render_target_op (RenderOpBuilder *builder,
+ int render_target_id)
+{
+ RenderOp op;
+ int prev_render_target;
+
+ if (builder->current_render_target == render_target_id)
+ return render_target_id;
+
+ prev_render_target = builder->current_render_target;
+ op.op = OP_CHANGE_RENDER_TARGET;
+ op.render_target_id = render_target_id;
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->current_render_target = render_target_id;
+
+ return prev_render_target;
+}
+
+static void
+add_draw_op (RenderOpBuilder *builder,
+ const GskQuadVertex vertex_data[N_VERTICES])
+{
+ RenderOp op;
+ gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
+
+ op.op = OP_CHANGE_VAO;
+ memcpy (&op.vertex_data, vertex_data, sizeof(GskQuadVertex) * N_VERTICES);
+ g_array_append_val (builder->renderer->render_ops, op);
+ builder->buffer_size += sizeof (GskQuadVertex) * N_VERTICES;
+
+ op.op = OP_DRAW;
+ op.vao_offset = offset;
+ g_array_append_val (builder->renderer->render_ops, op);
+}
+
+static void
+add_op (RenderOpBuilder *builder,
+ const RenderOp *op)
+{
+ g_array_append_val (builder->renderer->render_ops, *op);
+}
+
+static void
+gsk_gl_renderer_dispose (GObject *gobject)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (gobject);
+
+ g_clear_pointer (&self->render_ops, g_array_unref);
+
+ G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_gl_renderer_create_buffers (GskGLRenderer *self,
+ int width,
+ int height,
+ int scale_factor)
+{
+ if (self->has_buffers)
+ return;
+
+ GSK_NOTE (OPENGL, g_print ("Creating buffers (w:%d, h:%d, scale:%d)\n", width, height, scale_factor));
+
+ if (self->texture_id == 0)
+ {
+ self->texture_id = gsk_gl_driver_create_texture (self->gl_driver,
+ width * scale_factor,
+ height * scale_factor);
+ gsk_gl_driver_bind_source_texture (self->gl_driver, self->texture_id);
+ gsk_gl_driver_init_texture_empty (self->gl_driver, self->texture_id);
+ }
+
+ gsk_gl_driver_create_render_target (self->gl_driver, self->texture_id, TRUE, TRUE);
+ gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id);
+
+ self->has_buffers = TRUE;
+}
+
+static void
+gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
+{
+ if (self->gl_context == NULL)
+ return;
+
+ if (!self->has_buffers)
+ return;
+
+ GSK_NOTE (OPENGL, g_print ("Destroying buffers\n"));
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ if (self->texture_id != 0)
+ {
+ gsk_gl_driver_destroy_texture (self->gl_driver, self->texture_id);
+ self->texture_id = 0;
+ }
+
+ self->has_buffers = FALSE;
+}
+
+static void
+init_common_locations (GskGLRenderer *self,
+ GskShaderBuilder *builder,
+ Program *prog)
+{
+ prog->source_location = glGetUniformLocation (prog->id, "uSource");
+ prog->mask_location = glGetUniformLocation (prog->id, "uMask");
+ prog->alpha_location = glGetUniformLocation (prog->id, "uAlpha");
+ prog->blend_mode_location = glGetUniformLocation (prog->id, "uBlendMode");
+ prog->viewport_location = glGetUniformLocation (prog->id, "uViewport");
+ prog->projection_location = glGetUniformLocation (prog->id, "uProjection");
+ prog->modelview_location = glGetUniformLocation (prog->id, "uModelview");
+ prog->clip_location = glGetUniformLocation (prog->id, "uClip");
+ prog->clip_corner_widths_location = glGetUniformLocation (prog->id, "uClipCornerWidths");
+ prog->clip_corner_heights_location = glGetUniformLocation (prog->id, "uClipCornerHeights");
+
+ prog->position_location = glGetAttribLocation (prog->id, "aPosition");
+ prog->uv_location = glGetAttribLocation (prog->id, "aUv");
+}
+
+static gboolean
+gsk_gl_renderer_create_programs (GskGLRenderer *self,
+ GError **error)
+{
+ GskShaderBuilder *builder;
+ GError *shader_error = NULL;
+ gboolean res = FALSE;
+
+ builder = gsk_shader_builder_new ();
+
+ gsk_shader_builder_set_resource_base_path (builder, "/org/gtk/libgsk/glsl");
+
+ if (gdk_gl_context_get_use_es (self->gl_context))
+ {
+ gsk_shader_builder_set_version (builder, SHADER_VERSION_GLES);
+ gsk_shader_builder_set_vertex_preamble (builder, "es2_common.vs.glsl");
+ gsk_shader_builder_set_fragment_preamble (builder, "es2_common.fs.glsl");
+ gsk_shader_builder_add_define (builder, "GSK_GLES", "1");
+ }
+ else if (gdk_gl_context_is_legacy (self->gl_context))
+ {
+ int maj, min;
+ gdk_gl_context_get_version (self->gl_context, &maj, &min);
+
+ if (maj == 3)
+ gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3_LEGACY);
+ else
+ gsk_shader_builder_set_version (builder, SHADER_VERSION_GL2_LEGACY);
+
+ gsk_shader_builder_set_vertex_preamble (builder, "gl_common.vs.glsl");
+ gsk_shader_builder_set_fragment_preamble (builder, "gl_common.fs.glsl");
+ gsk_shader_builder_add_define (builder, "GSK_LEGACY", "1");
+ }
+ else
+ {
+ gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3);
+ gsk_shader_builder_set_vertex_preamble (builder, "gl3_common.vs.glsl");
+ gsk_shader_builder_set_fragment_preamble (builder, "gl3_common.fs.glsl");
+ gsk_shader_builder_add_define (builder, "GSK_GL3", "1");
+ }
+
+#ifdef G_ENABLE_DEBUG
+ if (GSK_RENDER_MODE_CHECK (SHADERS))
+ gsk_shader_builder_add_define (builder, "GSK_DEBUG", "1");
+#endif
+
+ self->blend_program.id = gsk_shader_builder_create_program (builder,
+ "blend.vs.glsl", "blend.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'blend' program: ");
+ goto out;
+ }
+ self->blend_program.index = 0;
+ init_common_locations (self, builder, &self->blend_program);
+
+ self->blit_program.id = gsk_shader_builder_create_program (builder,
+ "blit.vs.glsl", "blit.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'blit' program: ");
+ goto out;
+ }
+ self->blit_program.index = 1;
+ init_common_locations (self, builder, &self->blit_program);
+
+ self->color_program.id = gsk_shader_builder_create_program (builder,
+ "blit.vs.glsl", "color.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'color' program: ");
+ goto out;
+ }
+ self->color_program.index = 2;
+ init_common_locations (self, builder, &self->color_program);
+ INIT_PROGRAM_UNIFORM_LOCATION (color_program, color_location, "uColor");
+
+ self->coloring_program.id = gsk_shader_builder_create_program (builder,
+ "blit.vs.glsl", "coloring.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'coloring' program: ");
+ goto out;
+ }
+ self->coloring_program.index = 3;
+ init_common_locations (self, builder, &self->coloring_program);
+ INIT_PROGRAM_UNIFORM_LOCATION (coloring_program, color_location, "uColor");
+
+ self->color_matrix_program.id = gsk_shader_builder_create_program (builder,
+ "blit.vs.glsl", "color_matrix.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'color_matrix' program: ");
+ goto out;
+ }
+ self->color_matrix_program.index = 4;
+ init_common_locations (self, builder, &self->color_matrix_program);
+ INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_matrix_location, "uColorMatrix");
+ INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_offset_location, "uColorOffset");
+
+ self->linear_gradient_program.id = gsk_shader_builder_create_program (builder,
+ "blit.vs.glsl", "linear_gradient.fs.glsl",
+ &shader_error);
+ if (shader_error != NULL)
+ {
+ g_propagate_prefixed_error (error,
+ shader_error,
+ "Unable to create 'linear_gradient' program: ");
+ goto out;
+ }
+ self->linear_gradient_program.index = 5;
+ init_common_locations (self, builder, &self->linear_gradient_program);
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_stops_location, "uColorStops");
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_offsets_location, "uColorOffsets");
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, n_color_stops_location, "uNumColorStops");
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, start_point_location, "uStartPoint");
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, end_point_location, "uEndPoint");
+
+ res = TRUE;
+
+out:
+
+ g_object_unref (builder);
+ return res;
+}
+
+static gboolean
+gsk_gl_renderer_realize (GskRenderer *renderer,
+ GdkWindow *window,
+ GError **error)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ GskQuadVertex vertex_data[N_VERTICES] = {
+ { { 0, 0 }, { 0, 0 }, },
+ { { 0, 1 }, { 0, 1 }, },
+ { { 1, 0 }, { 1, 0 }, },
+
+ { { 1, 1 }, { 1, 1 }, },
+ { { 0, 1 }, { 0, 1 }, },
+ { { 1, 0 }, { 1, 0 }, },
+ };
+
+
+ self->scale_factor = gdk_window_get_scale_factor (window);
+
+ /* If we didn't get a GdkGLContext before realization, try creating
+ * one now, for our exclusive use.
+ */
+ if (self->gl_context == NULL)
+ {
+ self->gl_context = gdk_window_create_gl_context (window, error);
+ if (self->gl_context == NULL)
+ return FALSE;
+ }
+
+ if (!gdk_gl_context_realize (self->gl_context, error))
+ return FALSE;
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ g_assert (self->gl_driver == NULL);
+ self->gl_profiler = gsk_gl_profiler_new (self->gl_context);
+ self->gl_driver = gsk_gl_driver_new (self->gl_context);
+
+ GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
+ if (!gsk_gl_renderer_create_programs (self, error))
+ return FALSE;
+
+ gsk_gl_glyph_cache_init (&self->glyph_cache, self->gl_driver);
+
+ gsk_gl_driver_create_permanent_vao_for_quad (self->gl_driver, N_VERTICES, vertex_data,
+ &self->full_vao_id, &self->full_vao_buffer_id);
+
+ return TRUE;
+}
+
+static void
+gsk_gl_renderer_unrealize (GskRenderer *renderer)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ if (self->gl_context == NULL)
+ return;
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ /* We don't need to iterate to destroy the associated GL resources,
+ * as they will be dropped when we finalize the GskGLDriver
+ */
+ g_array_set_size (self->render_ops, 0);
+
+
+ glDeleteProgram (self->blend_program.id);
+ glDeleteProgram (self->blit_program.id);
+ glDeleteProgram (self->color_program.id);
+ glDeleteProgram (self->coloring_program.id);
+ glDeleteProgram (self->color_matrix_program.id);
+ glDeleteProgram (self->linear_gradient_program.id);
+
+ gsk_gl_renderer_destroy_buffers (self);
+
+ gsk_gl_glyph_cache_free (&self->glyph_cache);
+
+ g_clear_object (&self->gl_profiler);
+ g_clear_object (&self->gl_driver);
+
+ if (self->gl_context == gdk_gl_context_get_current ())
+ gdk_gl_context_clear_current ();
+
+ g_clear_object (&self->gl_context);
+}
+
+static GdkDrawingContext *
+gsk_gl_renderer_begin_draw_frame (GskRenderer *renderer,
+ const cairo_region_t *update_area)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ cairo_region_t *damage;
+ GdkDrawingContext *result;
+ GdkRectangle whole_window;
+ GdkWindow *window;
+
+ window = gsk_renderer_get_window (renderer);
+ whole_window = (GdkRectangle) {
+ 0, 0,
+ gdk_window_get_width (window) * self->scale_factor,
+ gdk_window_get_height (window) * self->scale_factor
+ };
+ damage = gdk_gl_context_get_damage (self->gl_context);
+ cairo_region_union (damage, update_area);
+
+ if (cairo_region_contains_rectangle (damage, &whole_window) == CAIRO_REGION_OVERLAP_IN)
+ {
+ self->render_mode = RENDER_FULL;
+ }
+ else
+ {
+ GdkRectangle extents;
+
+ cairo_region_get_extents (damage, &extents);
+ cairo_region_union_rectangle (damage, &extents);
+
+ if (gdk_rectangle_equal (&extents, &whole_window))
+ self->render_mode = RENDER_FULL;
+ else
+ self->render_mode = RENDER_SCISSOR;
+ }
+
+ result = gdk_window_begin_draw_frame (window,
+ GDK_DRAW_CONTEXT (self->gl_context),
+ damage);
+
+ cairo_region_destroy (damage);
+
+ return result;
+}
+
+static void
+gsk_gl_renderer_resize_viewport (GskGLRenderer *self,
+ const graphene_rect_t *viewport)
+{
+ int width = viewport->size.width;
+ int height = viewport->size.height;
+
+ GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %d, %d) [scale:%d]\n",
+ width,
+ height,
+ self->scale_factor));
+
+ graphene_rect_init (&self->viewport, 0, 0, width, height);
+ glViewport (0, 0, width, height);
+}
+
+
+static void
+get_gl_scaling_filters (GskRenderNode *node,
+ int *min_filter_r,
+ int *mag_filter_r)
+{
+ *min_filter_r = GL_NEAREST;
+ *mag_filter_r = GL_NEAREST;
+}
+
+static void
+gsk_gl_renderer_clear_tree (GskGLRenderer *self)
+{
+ int removed_textures, removed_vaos;
+
+ if (self->gl_context == NULL)
+ return;
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ g_array_remove_range (self->render_ops, 0, self->render_ops->len);
+
+ removed_textures = gsk_gl_driver_collect_textures (self->gl_driver);
+ removed_vaos = gsk_gl_driver_collect_vaos (self->gl_driver);
+
+ GSK_NOTE (OPENGL, g_print ("Collected: %d textures, %d vaos\n",
+ removed_textures,
+ removed_vaos));
+}
+
+static void
+gsk_gl_renderer_clear (GskGLRenderer *self)
+{
+ GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
+ glClearColor (0, 0, 0, 0);
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+}
+
+static void
+gsk_gl_renderer_setup_render_mode (GskGLRenderer *self)
+{
+ switch (self->render_mode)
+ {
+ case RENDER_FULL:
+ glDisable (GL_SCISSOR_TEST);
+ break;
+
+ case RENDER_SCISSOR:
+ {
+ GdkDrawingContext *context = gsk_renderer_get_drawing_context (GSK_RENDERER (self));
+ GdkWindow *window = gsk_renderer_get_window (GSK_RENDERER (self));
+ cairo_region_t *clip = gdk_drawing_context_get_clip (context);
+ cairo_rectangle_int_t extents;
+ int window_height;
+
+ /* Fall back to RENDER_FULL */
+ if (clip == NULL)
+ {
+ glDisable (GL_SCISSOR_TEST);
+ return;
+ }
+
+ g_assert (cairo_region_num_rectangles (clip) == 1);
+
+ window_height = gdk_window_get_height (window) * self->scale_factor;
+
+ /*cairo_region_get_extents (clip, &extents);*/
+ cairo_region_get_rectangle (clip, 0, &extents);
+
+ glEnable (GL_SCISSOR_TEST);
+ glScissor (extents.x * self->scale_factor,
+ window_height - (extents.height * self->scale_factor) - (extents.y * self->scale_factor),
+ extents.width * self->scale_factor,
+ extents.height * self->scale_factor);
+
+ cairo_region_destroy (clip);
+ break;
+ }
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+static void
+gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
+ GskRenderNode *node,
+ RenderOpBuilder *builder)
+{
+ float min_x = node->bounds.origin.x;
+ float min_y = node->bounds.origin.y;
+ float max_x = min_x + node->bounds.size.width;
+ float max_y = min_y + node->bounds.size.height;
+
+ /* Default vertex data */
+ GskQuadVertex vertex_data[N_VERTICES] = {
+ { { min_x, min_y }, { 0, 0 }, },
+ { { min_x, max_y }, { 0, 1 }, },
+ { { max_x, min_y }, { 1, 0 }, },
+
+ { { max_x, max_y }, { 1, 1 }, },
+ { { min_x, max_y }, { 0, 1 }, },
+ { { max_x, min_y }, { 1, 0 }, },
+ };
+
+ /*if (gsk_render_node_get_node_type (node) != GSK_CONTAINER_NODE)*/
+ /*g_message ("Adding ops for node %s with type %u", node->name,*/
+ /*gsk_render_node_get_node_type (node));*/
+
+
+ switch (gsk_render_node_get_node_type (node))
+ {
+ case GSK_NOT_A_RENDER_NODE:
+ g_assert_not_reached ();
+
+ case GSK_CONTAINER_NODE:
+ {
+ guint i, p;
+
+ for (i = 0, p = gsk_container_node_get_n_children (node); i < p; i ++)
+ {
+ GskRenderNode *child = gsk_container_node_get_child (node, i);
+
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+ }
+ }
+ break;
+
+ case GSK_COLOR_NODE:
+ {
+ RenderOp op;
+
+ add_program_op (builder, &self->color_program);
+ op.op = OP_CHANGE_COLOR;
+ op.color = *gsk_color_node_peek_color (node);
+ add_op (builder, &op);
+ add_draw_op (builder, vertex_data);
+ }
+ break;
+
+ case GSK_TEXTURE_NODE:
+ {
+ GdkTexture *texture = gsk_texture_node_get_texture (node);
+ int gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
+ int texture_id;
+
+ get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter);
+
+ texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver,
+ texture,
+ gl_min_filter,
+ gl_mag_filter);
+ add_program_op (builder, &self->blit_program);
+ add_texture_op (builder, texture_id);
+ add_draw_op (builder, vertex_data);
+ }
+ break;
+
+ case GSK_TRANSFORM_NODE:
+ {
+ GskRenderNode *child = gsk_transform_node_get_child (node);
+ graphene_matrix_t prev_mv;
+ graphene_matrix_t transform, transformed_mv;
+
+ graphene_matrix_init_from_matrix (&transform, gsk_transform_node_peek_transform (node));
+ graphene_matrix_multiply (&transform, &builder->current_modelview, &transformed_mv);
+ prev_mv = add_modelview_op (builder, &transformed_mv);
+
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+
+ add_modelview_op (builder, &prev_mv);
+ }
+ break;
+
+ case GSK_OPACITY_NODE:
+ {
+ int render_target;
+ int texture;
+ int prev_render_target;
+ float prev_opacity;
+ graphene_matrix_t identity;
+ graphene_matrix_t prev_projection;
+ graphene_matrix_t prev_modelview;
+ graphene_rect_t prev_viewport;
+ graphene_matrix_t item_proj;
+ GskQuadVertex vertex_data[N_VERTICES] = {
+ { { min_x, min_y }, { 0, 1 }, },
+ { { min_x, max_y }, { 0, 0 }, },
+ { { max_x, min_y }, { 1, 1 }, },
+
+ { { max_x, max_y }, { 1, 0 }, },
+ { { min_x, max_y }, { 0, 0 }, },
+ { { max_x, min_y }, { 1, 1 }, },
+ };
+
+ texture = gsk_gl_driver_create_texture (self->gl_driver,
+ node->bounds.size.width,
+ node->bounds.size.height);
+ gsk_gl_driver_bind_source_texture (self->gl_driver, texture);
+ gsk_gl_driver_init_texture_empty (self->gl_driver, texture);
+ render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture, TRUE, TRUE);
+
+ graphene_matrix_init_ortho (&item_proj,
+ min_x, max_x,
+ min_y, max_y,
+ ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
+ graphene_matrix_scale (&item_proj, 1, -1, 1);
+ graphene_matrix_init_identity (&identity);
+
+ prev_render_target = add_render_target_op (builder, render_target);
+ prev_projection = add_projection_op (builder, &item_proj);
+ prev_modelview = add_modelview_op (builder, &identity);
+ prev_viewport = add_viewport_op (builder, &node->bounds);
+
+ gsk_gl_renderer_add_render_ops (self, gsk_opacity_node_get_child (node), builder);
+
+ add_viewport_op (builder, &prev_viewport);
+ add_modelview_op (builder, &prev_modelview);
+ add_projection_op (builder, &prev_projection);
+ add_render_target_op (builder, prev_render_target);
+
+ add_program_op (builder, &self->blit_program);
+ prev_opacity = add_opacity_op (builder, gsk_opacity_node_get_opacity (node));
+ add_texture_op (builder, texture);
+ add_draw_op (builder, vertex_data);
+ add_opacity_op (builder, prev_opacity);
+ }
+ break;
+
+ case GSK_LINEAR_GRADIENT_NODE:
+ {
+ RenderOp op;
+ int n_color_stops = MIN (8, gsk_linear_gradient_node_get_n_color_stops (node));
+ const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
+ const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
+ const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node);
+ int i;
+
+ for (i = 0; i < n_color_stops; i ++)
+ {
+ const GskColorStop *stop = stops + i;
+
+ op.linear_gradient.color_stops[(i * 4) + 0] = stop->color.red;
+ op.linear_gradient.color_stops[(i * 4) + 1] = stop->color.green;
+ op.linear_gradient.color_stops[(i * 4) + 2] = stop->color.blue;
+ op.linear_gradient.color_stops[(i * 4) + 3] = stop->color.alpha;
+ op.linear_gradient.color_offsets[i] = stop->offset;
+ }
+
+ add_program_op (builder, &self->linear_gradient_program);
+ op.op = OP_CHANGE_LINEAR_GRADIENT;
+ op.linear_gradient.n_color_stops = n_color_stops;
+ op.linear_gradient.start_point = *start;
+ op.linear_gradient.end_point = *end;
+ add_op (builder, &op);
+
+ add_draw_op (builder, vertex_data);
+ }
+ break;
+
+ case GSK_CLIP_NODE:
+ {
+ GskRoundedRect prev_clip;
+ GskRenderNode *child = gsk_clip_node_get_child (node);
+ graphene_rect_t transformed_clip;
+ graphene_rect_t intersection;
+ GskRoundedRect child_clip;
+
+ transformed_clip = *gsk_clip_node_peek_clip (node);
+ graphene_matrix_transform_bounds (&builder->current_modelview, &transformed_clip, &transformed_clip);
+
+ graphene_rect_intersection (&transformed_clip,
+ &builder->current_clip.bounds,
+ &intersection);
+
+ gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f);
+
+ prev_clip = add_clip_op (builder, &child_clip);
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+ add_clip_op (builder, &prev_clip);
+ }
+ break;
+
+ case GSK_ROUNDED_CLIP_NODE:
+ {
+ GskRoundedRect prev_clip;
+ GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
+ const GskRoundedRect *rounded_clip = gsk_rounded_clip_node_peek_clip (node);
+ graphene_rect_t transformed_clip;
+ graphene_rect_t intersection;
+ GskRoundedRect child_clip;
+
+ transformed_clip = rounded_clip->bounds;
+ graphene_matrix_transform_bounds (&builder->current_modelview, &transformed_clip, &transformed_clip);
+
+ graphene_rect_intersection (&transformed_clip, &builder->current_clip.bounds,
+ &intersection);
+ gsk_rounded_rect_init (&child_clip, &intersection,
+ &rounded_clip->corner[0],
+ &rounded_clip->corner[1],
+ &rounded_clip->corner[2],
+ &rounded_clip->corner[3]);
+
+ prev_clip = add_clip_op (builder, &child_clip);
+ gsk_gl_renderer_add_render_ops (self, child, builder);
+ add_clip_op (builder, &prev_clip);
+ }
+ break;
+
+ case GSK_TEXT_NODE:
+ {
+ const PangoFont *font = gsk_text_node_peek_font (node);
+ const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+ guint num_glyphs = gsk_text_node_get_num_glyphs (node);
+ int i;
+ int x_position = 0;
+ int x = gsk_text_node_get_x (node);
+ int y = gsk_text_node_get_y (node);
+
+ /* We use one quad per character, unlike the other nodes which
+ * use at most one quad altogether */
+ for (i = 0; i < num_glyphs; i++)
+ {
+ const PangoGlyphInfo *gi = &glyphs[i];
+ const GskGLCachedGlyph *glyph;
+ int glyph_x, glyph_y, glyph_w, glyph_h;
+ float tx, ty, tx2, ty2;
+ double cx;
+ double cy;
+
+ if (gi->glyph == PANGO_GLYPH_EMPTY ||
+ (gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG) > 0)
+ continue;
+
+ glyph = gsk_gl_glyph_cache_lookup (&self->glyph_cache,
+ TRUE,
+ (PangoFont *)font,
+ gi->glyph,
+ self->scale_factor);
+
+ /* e.g. whitespace */
+ if (glyph->draw_width <= 0 || glyph->draw_height <= 0)
+ {
+ x_position += gi->geometry.width;
+ continue;
+ }
+ cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
+ cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
+
+ /* If the font has color glyphs, we don't need to recolor anything */
+ if (font_has_color_glyphs (font))
+ {
+ add_program_op (builder, &self->blit_program);
+ }
+ else
+ {
+ RenderOp op;
+
+ add_program_op (builder, &self->coloring_program);
+
+ op.op = OP_CHANGE_COLOR;
+ op.color = *gsk_text_node_peek_color (node);
+ add_op (builder, &op);
+ }
+
+ add_texture_op (builder, gsk_gl_glyph_cache_get_glyph_image (&self->glyph_cache,
+ glyph)->texture_id);
+
+ {
+ tx = glyph->tx;
+ ty = glyph->ty;
+ tx2 = tx + glyph->tw;
+ ty2 = ty + glyph->th;
+
+ glyph_x = x + cx + glyph->draw_x;
+ glyph_y = y + cy + glyph->draw_y;
+ glyph_w = glyph->draw_width;
+ glyph_h = glyph->draw_height;
+
+ GskQuadVertex vertex_data[N_VERTICES] = {
+ { { glyph_x, glyph_y }, { tx, ty }, },
+ { { glyph_x, glyph_y + glyph_h }, { tx, ty2 }, },
+ { { glyph_x + glyph_w, glyph_y }, { tx2, ty }, },
+
+ { { glyph_x + glyph_w, glyph_y + glyph_h }, { tx2, ty2 }, },
+ { { glyph_x, glyph_y + glyph_h }, { tx, ty2 }, },
+ { { glyph_x + glyph_w, glyph_y }, { tx2, ty }, },
+ };
+
+ add_draw_op (builder, vertex_data);
+ }
+
+ x_position += gi->geometry.width;
+ }
+
+ }
+ break;
+
+
+ case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+ case GSK_BORDER_NODE:
+ case GSK_INSET_SHADOW_NODE:
+ case GSK_OUTSET_SHADOW_NODE:
+ case GSK_BLUR_NODE:
+ case GSK_SHADOW_NODE:
+ case GSK_CROSS_FADE_NODE:
+ case GSK_BLEND_NODE:
+ case GSK_REPEAT_NODE:
+ case GSK_CAIRO_NODE:
+ case GSK_COLOR_MATRIX_NODE:
+ default:
+ {
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int texture_id;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ ceilf (node->bounds.size.width) * self->scale_factor,
+ ceilf (node->bounds.size.height) * self->scale_factor);
+ cairo_surface_set_device_scale (surface, self->scale_factor, self->scale_factor);
+ cr = cairo_create (surface);
+
+ cairo_save (cr);
+ cairo_translate (cr, -min_x, -min_y);
+ gsk_render_node_draw (node, cr);
+ cairo_restore (cr);
+
+#if HIGHLIGHT_FALLBACK
+ cairo_move_to (cr, 0, 0);
+ cairo_rectangle (cr, 0, 0, max_x - min_x, max_y - min_y);
+ cairo_set_source_rgba (cr, 1, 0, 0, 1);
+ cairo_stroke (cr);
+#endif
+ cairo_destroy (cr);
+
+ /* Upload the Cairo surface to a GL texture */
+ texture_id = gsk_gl_driver_create_texture (self->gl_driver,
+ node->bounds.size.width * self->scale_factor,
+ node->bounds.size.height * self->scale_factor);
+
+ gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
+ gsk_gl_driver_init_texture_with_surface (self->gl_driver,
+ texture_id,
+ surface,
+ GL_NEAREST, GL_NEAREST);
+
+ cairo_surface_destroy (surface);
+
+ add_program_op (builder, &self->blit_program);
+ add_texture_op (builder, texture_id);
+ add_draw_op (builder, vertex_data);
+ }
+ }
+}
+
+static void
+gsk_gl_renderer_render_ops (GskGLRenderer *self,
+ gsize vertex_data_size)
+{
+ guint i;
+ guint n_ops = self->render_ops->len;
+ float mat[16];
+ const Program *program = NULL;
+ gsize buffer_index = 0;
+ float *vertex_data = g_malloc (vertex_data_size);
+
+ /*g_message ("%s: Buffer size: %ld", __FUNCTION__, vertex_data_size);*/
+
+
+ GLuint buffer_id, vao_id;
+ glGenVertexArrays (1, &vao_id);
+ glBindVertexArray (vao_id);
+
+ glGenBuffers (1, &buffer_id);
+ glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
+
+
+ // Fill buffer data
+ for (i = 0; i < n_ops; i ++)
+ {
+ const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
+
+ if (op->op == OP_CHANGE_VAO)
+ {
+ memcpy (vertex_data + buffer_index, &op->vertex_data, sizeof (GskQuadVertex) * N_VERTICES);
+ buffer_index += sizeof (GskQuadVertex) * N_VERTICES / sizeof (float);
+ }
+ }
+
+ // Set buffer data
+ glBufferData (GL_ARRAY_BUFFER, vertex_data_size, vertex_data, GL_STATIC_DRAW);
+
+ // Describe buffer contents
+
+ /* 0 = position location */
+ glEnableVertexAttribArray (0);
+ glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskQuadVertex),
+ (void *) G_STRUCT_OFFSET (GskQuadVertex, position));
+ /* 1 = texture coord location */
+ glEnableVertexAttribArray (1);
+ glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE,
+ sizeof (GskQuadVertex),
+ (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
+
+ for (i = 0; i < n_ops; i ++)
+ {
+ const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
+
+ if (op->op == OP_NONE ||
+ op->op == OP_CHANGE_VAO)
+ continue;
+
+ OP_PRINT ("Op %u: %u", i, op->op);
+
+ switch (op->op)
+ {
+ case OP_CHANGE_PROJECTION:
+ graphene_matrix_to_float (&op->projection, mat);
+ glUniformMatrix4fv (program->projection_location, 1, GL_FALSE, mat);
+ OP_PRINT (" -> Projection");
+ /*graphene_matrix_print (&op->projection);*/
+ break;
+
+ case OP_CHANGE_MODELVIEW:
+ graphene_matrix_to_float (&op->modelview, mat);
+ glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat);
+ OP_PRINT (" -> Modelview");
+ /*graphene_matrix_print (&op->modelview);*/
+ break;
+
+ case OP_CHANGE_PROGRAM:
+ program = op->program;
+ glUseProgram (op->program->id);
+ OP_PRINT (" -> Program: %d", op->program->id);
+ break;
+
+ case OP_CHANGE_RENDER_TARGET:
+ OP_PRINT (" -> Render Target: %d", op->render_target_id);
+
+ glBindFramebuffer (GL_FRAMEBUFFER, op->render_target_id);
+ if (op->render_target_id != 0)
+ {
+ glDisable (GL_SCISSOR_TEST);
+ glClearColor (0.0, 0.0, 0.0, 0.0);
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+ else
+ {
+ /* Reset glScissor, etc. */
+ gsk_gl_renderer_setup_render_mode (self);
+ }
+ break;
+
+ case OP_CHANGE_VIEWPORT:
+ OP_PRINT (" -> New Viewport: %f, %f, %f, %f", op->viewport.origin.x, op->viewport.origin.y, op->viewport.size.width, op->viewport.size.height);
+ glUniform4f (program->viewport_location,
+ op->viewport.origin.x, op->viewport.origin.y,
+ op->viewport.size.width, op->viewport.size.height);
+ glViewport (0, 0, op->viewport.size.width, op->viewport.size.height);
+ break;
+
+ case OP_CHANGE_OPACITY:
+ glUniform1f (program->alpha_location, op->opacity);
+ break;
+
+ case OP_CHANGE_COLOR:
+ OP_PRINT (" -> Color: (%f, %f, %f, %f)", op->color.red, op->color.green, op->color.blue, op->color.alpha);
+ g_assert (program == &self->color_program || program == &self->coloring_program);
+ glUniform4f (program->color_location,
+ op->color.red, op->color.green, op->color.blue, op->color.alpha);
+ break;
+
+ case OP_CHANGE_CLIP:
+ OP_PRINT (" -> Clip");
+ glUniform4f (program->clip_location,
+ op->clip.bounds.origin.x, op->clip.bounds.origin.y,
+ op->clip.bounds.size.width, op->clip.bounds.size.height);
+
+ glUniform4f (program->clip_corner_widths_location,
+ MAX (op->clip.corner[0].width, 1),
+ MAX (op->clip.corner[1].width, 1),
+ MAX (op->clip.corner[2].width, 1),
+ MAX (op->clip.corner[3].width, 1));
+ glUniform4f (program->clip_corner_heights_location,
+ MAX (op->clip.corner[0].height, 1),
+ MAX (op->clip.corner[1].height, 1),
+ MAX (op->clip.corner[2].height, 1),
+ MAX (op->clip.corner[3].height, 1));
+ break;
+
+ case OP_CHANGE_SOURCE_TEXTURE:
+ g_assert(op->texture_id != 0);
+ OP_PRINT (" -> New texture: %d", op->texture_id);
+ /* Use texture unit 0 for the source */
+ glUniform1i (program->source_location, 0);
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, op->texture_id);
+
+ break;
+
+ case OP_CHANGE_LINEAR_GRADIENT:
+ OP_PRINT (" -> Linear gradient");
+ glUniform1i (program->n_color_stops_location,
+ op->linear_gradient.n_color_stops);
+ glUniform4fv (program->color_stops_location,
+ op->linear_gradient.n_color_stops,
+ op->linear_gradient.color_stops);
+ glUniform1fv (program->color_offsets_location,
+ op->linear_gradient.n_color_stops,
+ op->linear_gradient.color_offsets);
+ glUniform2f (program->start_point_location,
+ op->linear_gradient.start_point.x, op->linear_gradient.start_point.y);
+ glUniform2f (program->end_point_location,
+ op->linear_gradient.end_point.x, op->linear_gradient.end_point.y);
+ break;
+
+ case OP_DRAW:
+ OP_PRINT (" -> draw %ld\n", op->vao_offset);
+ glDrawArrays (GL_TRIANGLES, op->vao_offset, N_VERTICES);
+ break;
+
+ default:
+ g_warn_if_reached ();
+ }
+
+ OP_PRINT ("\n");
+ }
+
+ /* Done drawing, destroy the buffer again.
+ * TODO: Can we reuse the memory, though? */
+ g_free (vertex_data);
+}
+
+static void
+gsk_gl_renderer_do_render (GskRenderer *renderer,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport,
+ int scale_factor)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ RenderOpBuilder render_op_builder;
+ graphene_matrix_t modelview, projection;
+#ifdef G_ENABLE_DEBUG
+ GskProfiler *profiler;
+ gint64 gpu_time, cpu_time;
+#endif
+
+#ifdef G_ENABLE_DEBUG
+ profiler = gsk_renderer_get_profiler (renderer);
+#endif
+
+ if (self->gl_context == NULL)
+ {
+ GSK_NOTE (OPENGL, g_print ("No valid GL context associated to the renderer"));
+ return;
+ }
+
+ self->viewport = *viewport;
+
+ /* Set up the modelview and projection matrices to fit our viewport */
+ graphene_matrix_init_scale (&modelview, scale_factor, scale_factor, 1.0);
+ graphene_matrix_init_ortho (&projection,
+ viewport->origin.x,
+ viewport->origin.x + viewport->size.width,
+ viewport->origin.y,
+ viewport->origin.y + viewport->size.height,
+ ORTHO_NEAR_PLANE,
+ ORTHO_FAR_PLANE);
+
+ if (self->texture_id == 0)
+ graphene_matrix_scale (&projection, 1, -1, 1);
+
+ gsk_gl_driver_begin_frame (self->gl_driver);
+ gsk_gl_glyph_cache_begin_frame (&self->glyph_cache);
+
+ memset (&render_op_builder, 0, sizeof (render_op_builder));
+ render_op_builder.renderer = self;
+ render_op_builder.current_projection = projection;
+ render_op_builder.current_modelview = modelview;
+ render_op_builder.current_viewport = *viewport;
+ render_op_builder.current_render_target = self->texture_id;
+ render_op_builder.current_opacity = 1.0f;
+ gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, &self->viewport, 0.0f);
+ gsk_gl_renderer_add_render_ops (self, root, &render_op_builder);
+
+ /*g_message ("Ops: %u", self->render_ops->len);*/
+
+ /* Now actually draw things... */
+#ifdef G_ENABLE_DEBUG
+ gsk_gl_profiler_begin_gpu_region (self->gl_profiler);
+ gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
+#endif
+
+ gsk_gl_renderer_resize_viewport (self, viewport);
+ gsk_gl_renderer_setup_render_mode (self);
+ gsk_gl_renderer_clear (self);
+
+ glEnable (GL_DEPTH_TEST);
+ glDepthFunc (GL_LEQUAL);
+
+ /* Pre-multiplied alpha! */
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendEquation (GL_FUNC_ADD);
+
+ gsk_gl_renderer_render_ops (self, render_op_builder.buffer_size);
+
+ gsk_gl_driver_end_frame (self->gl_driver);
+
+#ifdef G_ENABLE_DEBUG
+ gsk_profiler_counter_inc (profiler, self->profile_counters.frames);
+
+ cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
+ gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
+
+ gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler);
+ gsk_profiler_timer_set (profiler, self->profile_timers.gpu_time, gpu_time);
+
+ gsk_profiler_push_samples (profiler);
+#endif
+}
+
+static GdkTexture *
+gsk_gl_renderer_render_texture (GskRenderer *renderer,
+ GskRenderNode *root,
+ const graphene_rect_t *viewport)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ GdkTexture *texture;
+ int stride;
+ guchar *data;
+ int width, height;
+
+ g_return_val_if_fail (self->gl_context != NULL, NULL);
+
+ self->render_mode = RENDER_FULL;
+ width = ceilf (viewport->size.width);
+ height = ceilf (viewport->size.height);
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ /* Prepare our framebuffer */
+ gsk_gl_driver_begin_frame (self->gl_driver);
+ gsk_gl_renderer_create_buffers (self, width, height, 1);
+ gsk_gl_renderer_clear (self);
+ gsk_gl_driver_end_frame (self->gl_driver);
+
+ /* Render the actual scene */
+ gsk_gl_renderer_do_render (renderer, root, viewport, 1);
+
+ /* Prepare memory for the glReadPixels call */
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+ data = g_malloc (height * stride);
+
+ /* Bind our framebuffer again and read from it */
+ gsk_gl_driver_begin_frame (self->gl_driver);
+ gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id);
+ glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+ gsk_gl_driver_end_frame (self->gl_driver);
+
+ /* Create texture from the downloaded data */
+ texture = gdk_texture_new_for_data (data, width, height, stride);
+
+ return texture;
+}
+
+static void
+gsk_gl_renderer_render (GskRenderer *renderer,
+ GskRenderNode *root)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ GdkWindow *window = gsk_renderer_get_window (renderer);
+ graphene_rect_t viewport;
+
+ if (self->gl_context == NULL)
+ return;
+
+ gdk_gl_context_make_current (self->gl_context);
+
+ viewport.origin.x = 0;
+ viewport.origin.y = 0;
+ viewport.size.width = gdk_window_get_width (window) * self->scale_factor;
+ viewport.size.height = gdk_window_get_height (window) * self->scale_factor;
+
+ gsk_gl_renderer_do_render (renderer, root, &viewport, self->scale_factor);
+
+ gdk_gl_context_make_current (self->gl_context);
+ gsk_gl_renderer_clear_tree (self);
+ gsk_gl_renderer_destroy_buffers (self);
+}
+
+static void
+gsk_gl_renderer_class_init (GskGLRendererClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
+
+ gobject_class->dispose = gsk_gl_renderer_dispose;
+
+ renderer_class->realize = gsk_gl_renderer_realize;
+ renderer_class->unrealize = gsk_gl_renderer_unrealize;
+ renderer_class->begin_draw_frame = gsk_gl_renderer_begin_draw_frame;
+ renderer_class->render = gsk_gl_renderer_render;
+ renderer_class->render_texture = gsk_gl_renderer_render_texture;
+}
+
+static void
+gsk_gl_renderer_init (GskGLRenderer *self)
+{
+ gsk_ensure_resources ();
+
+
+ self->scale_factor = 1;
+ self->render_ops = g_array_new (TRUE, FALSE, sizeof (RenderOp));
+
+#ifdef G_ENABLE_DEBUG
+ {
+ GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
+
+ self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE);
+ self->profile_counters.draw_calls = gsk_profiler_add_counter (profiler, "draws", "glDrawArrays", TRUE);
+
+ self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE);
+ self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE);
+ }
+#endif
+}
--- /dev/null
+#ifndef __GSK_GL_RENDERER_PRIVATE_H__
+#define __GSK_GL_RENDERER_PRIVATE_H__
+
+#include <gsk/gskrenderer.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ())
+
+#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer))
+#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER))
+#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
+#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER))
+#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
+
+typedef struct _GskGLRenderer GskGLRenderer;
+typedef struct _GskGLRendererClass GskGLRendererClass;
+
+GType gsk_gl_renderer_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */
+++ /dev/null
-#include "config.h"
-
-#include "gskgldriverprivate.h"
-
-#include "gskdebugprivate.h"
-#include "gskprofilerprivate.h"
-#include "gdk/gdktextureprivate.h"
-
-#include <gdk/gdk.h>
-#include <epoxy/gl.h>
-
-typedef struct {
- GLuint texture_id;
- int width;
- int height;
- GLuint min_filter;
- GLuint mag_filter;
- GArray *fbos;
- GdkTexture *user;
- guint in_use : 1;
- guint permanent : 1;
-} Texture;
-
-typedef struct {
- GLuint vao_id;
- GLuint buffer_id;
- GLuint position_id;
- GLuint uv_id;
- GskQuadVertex *quads;
- int n_quads;
- guint in_use : 1;
- guint permanent : 1;
-} Vao;
-
-typedef struct {
- GLuint fbo_id;
- GLuint depth_stencil_id;
-} Fbo;
-
-struct _GskGLDriver
-{
- GObject parent_instance;
-
- GdkGLContext *gl_context;
- GskProfiler *profiler;
- struct {
- GQuark created_textures;
- GQuark reused_textures;
- GQuark surface_uploads;
- } counters;
-
- Fbo default_fbo;
-
- GHashTable *textures;
- GHashTable *vaos;
-
- const Texture *bound_source_texture;
- const Texture *bound_mask_texture;
- const Vao *bound_vao;
- const Fbo *bound_fbo;
-
- int max_texture_size;
-
- gboolean in_frame : 1;
-};
-
-enum
-{
- PROP_GL_CONTEXT = 1,
-
- N_PROPS
-};
-
-static GParamSpec *gsk_gl_driver_properties[N_PROPS];
-
-G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
-
-static Texture *
-texture_new (void)
-{
- return g_slice_new0 (Texture);
-}
-
-static void
-texture_free (gpointer data)
-{
- Texture *t = data;
-
- if (t->user)
- gdk_texture_clear_render_data (t->user);
-
- g_clear_pointer (&t->fbos, g_array_unref);
- glDeleteTextures (1, &t->texture_id);
- g_slice_free (Texture, t);
-}
-
-static void
-fbo_clear (gpointer data)
-{
- Fbo *f = data;
-
- if (f->depth_stencil_id != 0)
- glDeleteRenderbuffers (1, &f->depth_stencil_id);
-
- glDeleteFramebuffers (1, &f->fbo_id);
-}
-
-static Vao *
-vao_new (void)
-{
- return g_slice_new0 (Vao);
-}
-
-static void
-vao_free (gpointer data)
-{
- Vao *v = data;
-
- g_free (v->quads);
- glDeleteBuffers (1, &v->buffer_id);
- glDeleteVertexArrays (1, &v->vao_id);
- g_slice_free (Vao, v);
-}
-
-static void
-gsk_gl_driver_finalize (GObject *gobject)
-{
- GskGLDriver *self = GSK_GL_DRIVER (gobject);
-
- gdk_gl_context_make_current (self->gl_context);
-
- g_clear_pointer (&self->textures, g_hash_table_unref);
- g_clear_pointer (&self->vaos, g_hash_table_unref);
- g_clear_object (&self->profiler);
-
- if (self->gl_context == gdk_gl_context_get_current ())
- gdk_gl_context_clear_current ();
-
- g_clear_object (&self->gl_context);
-
- G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject);
-}
-
-static void
-gsk_gl_driver_set_property (GObject *gobject,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GskGLDriver *self = GSK_GL_DRIVER (gobject);
-
- switch (prop_id)
- {
- case PROP_GL_CONTEXT:
- self->gl_context = g_value_dup_object (value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
- }
-}
-
-static void
-gsk_gl_driver_get_property (GObject *gobject,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GskGLDriver *self = GSK_GL_DRIVER (gobject);
-
- switch (prop_id)
- {
- case PROP_GL_CONTEXT:
- g_value_set_object (value, self->gl_context);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
- }
-}
-
-static void
-gsk_gl_driver_class_init (GskGLDriverClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->set_property = gsk_gl_driver_set_property;
- gobject_class->get_property = gsk_gl_driver_get_property;
- gobject_class->finalize = gsk_gl_driver_finalize;
-
- gsk_gl_driver_properties[PROP_GL_CONTEXT] =
- g_param_spec_object ("gl-context", "GL Context", "The GL context used by the driver",
- GDK_TYPE_GL_CONTEXT,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (gobject_class, N_PROPS, gsk_gl_driver_properties);
-}
-
-static void
-gsk_gl_driver_init (GskGLDriver *self)
-{
- self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free);
- self->vaos = g_hash_table_new_full (NULL, NULL, NULL, vao_free);
-
- self->max_texture_size = -1;
-
-#ifdef G_ENABLE_DEBUG
- self->profiler = gsk_profiler_new ();
- self->counters.created_textures = gsk_profiler_add_counter (self->profiler,
- "created_textures",
- "Textures created this frame",
- TRUE);
- self->counters.reused_textures = gsk_profiler_add_counter (self->profiler,
- "reused_textures",
- "Textures reused this frame",
- TRUE);
- self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler,
- "surface_uploads",
- "Texture uploads from surfaces this frame",
- TRUE);
-#endif
-}
-
-GskGLDriver *
-gsk_gl_driver_new (GdkGLContext *context)
-{
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
-
- return g_object_new (GSK_TYPE_GL_DRIVER,
- "gl-context", context,
- NULL);
-}
-
-void
-gsk_gl_driver_begin_frame (GskGLDriver *self)
-{
- g_return_if_fail (GSK_IS_GL_DRIVER (self));
- g_return_if_fail (!self->in_frame);
-
- self->in_frame = TRUE;
-
- if (self->max_texture_size < 0)
- {
- glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size);
- GSK_NOTE (OPENGL, g_print ("GL max texture size: %d\n", self->max_texture_size));
- }
-
- glBindFramebuffer (GL_FRAMEBUFFER, 0);
- self->bound_fbo = &self->default_fbo;
-
- glActiveTexture (GL_TEXTURE0);
- glBindTexture (GL_TEXTURE_2D, 0);
-
- glActiveTexture (GL_TEXTURE0 + 1);
- glBindTexture (GL_TEXTURE_2D, 0);
-
- glBindVertexArray (0);
- glUseProgram (0);
-
- glActiveTexture (GL_TEXTURE0);
-
- gsk_profiler_reset (self->profiler);
-}
-
-void
-gsk_gl_driver_end_frame (GskGLDriver *self)
-{
- g_return_if_fail (GSK_IS_GL_DRIVER (self));
- g_return_if_fail (self->in_frame);
-
- self->bound_source_texture = NULL;
- self->bound_mask_texture = NULL;
- self->bound_vao = NULL;
- self->bound_fbo = NULL;
-
- self->default_fbo.fbo_id = 0;
-
- GSK_NOTE (OPENGL,
- g_print ("Textures created: %ld\n"
- " Textures reused: %ld\n"
- " Surface uploads: %ld\n",
- gsk_profiler_counter_get (self->profiler, self->counters.created_textures),
- gsk_profiler_counter_get (self->profiler, self->counters.reused_textures),
- gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads)));
- GSK_NOTE (OPENGL,
- g_print ("*** Frame end: textures=%d, vaos=%d\n",
- g_hash_table_size (self->textures),
- g_hash_table_size (self->vaos)));
-
- self->in_frame = FALSE;
-}
-
-int
-gsk_gl_driver_collect_textures (GskGLDriver *driver)
-{
- GHashTableIter iter;
- gpointer value_p = NULL;
- int old_size;
- /*return;*/
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), 0);
- g_return_val_if_fail (!driver->in_frame, 0);
-
- old_size = g_hash_table_size (driver->textures);
-
- g_hash_table_iter_init (&iter, driver->textures);
- while (g_hash_table_iter_next (&iter, NULL, &value_p))
- {
- Texture *t = value_p;
-
- if (t->user || t->permanent)
- continue;
-
- if (t->in_use)
- {
- t->in_use = FALSE;
- g_clear_pointer (&t->fbos, g_array_unref);
- }
- else
- g_hash_table_iter_remove (&iter);
- }
-
- return old_size - g_hash_table_size (driver->textures);
-}
-
-int
-gsk_gl_driver_collect_vaos (GskGLDriver *driver)
-{
- GHashTableIter iter;
- gpointer value_p = NULL;
- int old_size;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), 0);
- g_return_val_if_fail (!driver->in_frame, 0);
-
- old_size = g_hash_table_size (driver->vaos);
-
- g_hash_table_iter_init (&iter, driver->vaos);
- while (g_hash_table_iter_next (&iter, NULL, &value_p))
- {
- Vao *v = value_p;
-
- if (v->in_use)
- v->in_use = FALSE;
- else
- g_hash_table_iter_remove (&iter);
- }
-
- return old_size - g_hash_table_size (driver->vaos);
-}
-
-int
-gsk_gl_driver_get_max_texture_size (GskGLDriver *driver)
-{
- if (driver->max_texture_size < 0)
- {
- if (gdk_gl_context_get_use_es (driver->gl_context))
- return 2048;
-
- return 1024;
- }
-
- return driver->max_texture_size;
-}
-
-static Texture *
-gsk_gl_driver_get_texture (GskGLDriver *driver,
- int texture_id)
-{
- Texture *t;
-
- if (g_hash_table_lookup_extended (driver->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t))
- return t;
-
- return NULL;
-}
-
-static Vao *
-gsk_gl_driver_get_vao (GskGLDriver *driver,
- int vao_id)
-{
- Vao *v;
-
- if (g_hash_table_lookup_extended (driver->vaos, GINT_TO_POINTER (vao_id), NULL, (gpointer *) &v))
- return v;
-
- return NULL;
-}
-
-static Fbo *
-gsk_gl_driver_get_fbo (GskGLDriver *driver,
- int texture_id)
-{
- Texture *t = gsk_gl_driver_get_texture (driver, texture_id);
-
- if (t->fbos == NULL)
- return &driver->default_fbo;
-
- return &g_array_index (t->fbos, Fbo, 0);
-}
-
-static Texture *
-find_texture_by_size (GHashTable *textures,
- int width,
- int height)
-{
- GHashTableIter iter;
- gpointer value_p = NULL;
-
- g_hash_table_iter_init (&iter, textures);
- while (g_hash_table_iter_next (&iter, NULL, &value_p))
- {
- Texture *t = value_p;
-
- if (t->width == width && t->height == height)
- return t;
- }
-
- return NULL;
-}
-
-static Texture *
-create_texture (GskGLDriver *self,
- float fwidth,
- float fheight)
-{
- guint texture_id;
- Texture *t;
- int width = ceilf (fwidth);
- int height = ceilf (fheight);
-
- if (width >= self->max_texture_size ||
- height >= self->max_texture_size)
- {
- g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
- width, height,
- self->max_texture_size);
-
- width = MIN (width, self->max_texture_size);
- height = MIN (height, self->max_texture_size);
- }
-
- t = find_texture_by_size (self->textures, width, height);
- if (t != NULL && !t->in_use && t->user == NULL)
- {
- GSK_NOTE (OPENGL, g_print ("Reusing Texture(%d) for size %dx%d\n",
- t->texture_id, t->width, t->height));
- t->in_use = TRUE;
- gsk_profiler_counter_inc (self->profiler, self->counters.reused_textures);
- return t;
- }
-
- glGenTextures (1, &texture_id);
-
- t = texture_new ();
- t->texture_id = texture_id;
- t->width = width;
- t->height = height;
- t->min_filter = GL_NEAREST;
- t->mag_filter = GL_NEAREST;
- t->in_use = TRUE;
- g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t);
- gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
-
- return t;
-}
-
-static void
-gsk_gl_driver_release_texture (gpointer data)
-{
- Texture *t = data;
-
- t->user = NULL;
-}
-
-int
-gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
- GdkTexture *texture,
- int min_filter,
- int mag_filter)
-{
- Texture *t;
- cairo_surface_t *surface;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
- g_return_val_if_fail (GDK_IS_TEXTURE (texture), -1);
-
- t = gdk_texture_get_render_data (texture, driver);
-
- if (t)
- {
- if (t->min_filter == min_filter && t->mag_filter == mag_filter)
- return t->texture_id;
- }
-
- t = create_texture (driver, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
-
- if (gdk_texture_set_render_data (texture, driver, t, gsk_gl_driver_release_texture))
- t->user = texture;
-
- surface = gdk_texture_download_surface (texture);
- gsk_gl_driver_bind_source_texture (driver, t->texture_id);
- gsk_gl_driver_init_texture_with_surface (driver,
- t->texture_id,
- surface,
- min_filter,
- mag_filter);
- cairo_surface_destroy (surface);
-
- return t->texture_id;
-}
-
-int
-gsk_gl_driver_create_permanent_texture (GskGLDriver *self,
- float width,
- float height)
-{
- Texture *t;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1);
-
- t = create_texture (self, width, height);
- t->permanent = TRUE;
-
- return t->texture_id;
-}
-
-int
-gsk_gl_driver_create_texture (GskGLDriver *driver,
- float width,
- float height)
-{
- Texture *t;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
-
- t = create_texture (driver, width, height);
-
- return t->texture_id;
-}
-
-static Vao *
-find_vao (GHashTable *vaos,
- int position_id,
- int uv_id,
- int n_quads,
- GskQuadVertex *quads)
-{
- GHashTableIter iter;
- gpointer value_p = NULL;
-
- g_hash_table_iter_init (&iter, vaos);
- while (g_hash_table_iter_next (&iter, NULL, &value_p))
- {
- Vao *v = value_p;
-
- if (v->position_id != position_id || v->uv_id != uv_id)
- continue;
-
- if (v->n_quads != n_quads)
- continue;
-
- if (memcmp (v->quads, quads, sizeof (GskQuadVertex) * n_quads) == 0)
- return v;
- }
-
- return NULL;
-}
-
-void
-gsk_gl_driver_create_permanent_vao_for_quad (GskGLDriver *driver,
- int n_vertices,
- const GskQuadVertex *quads,
- int *out_vao_id,
- int *out_vao_buffer_id)
-{
- GLuint vao_id, buffer_id;
-
- glGenVertexArrays (1, &vao_id);
- glBindVertexArray (vao_id);
-
- glGenBuffers (1, &buffer_id);
- glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
- glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_vertices, quads, GL_STATIC_DRAW);
-
- glBindBuffer (GL_ARRAY_BUFFER, 0);
- glBindVertexArray (0);
-
- *out_vao_id = buffer_id;
- *out_vao_buffer_id = vao_id;
-}
-
-int
-gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver,
- int position_id,
- int uv_id,
- int n_vertices,
- GskQuadVertex *quads)
-
-{
- GLuint vao_id, buffer_id;
- Vao *v;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
- g_return_val_if_fail (driver->in_frame, -1);
-
- v = find_vao (driver->vaos, position_id, uv_id, n_vertices, quads);
- if (v != NULL && !v->in_use)
- {
- GSK_NOTE (OPENGL, g_print ("Reusing VAO(%d)\n", v->vao_id));
- v->in_use = TRUE;
- return v->vao_id;
- }
-
- glGenVertexArrays (1, &vao_id);
- glBindVertexArray (vao_id);
-
- glGenBuffers (1, &buffer_id);
- glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
- glBufferData (GL_ARRAY_BUFFER, sizeof (GskQuadVertex) * n_vertices, quads, GL_STATIC_DRAW);
-
- if (position_id != -1)
- {
- glEnableVertexAttribArray (position_id);
- glVertexAttribPointer (position_id, 2, GL_FLOAT, GL_FALSE,
- sizeof (GskQuadVertex),
- (void *) G_STRUCT_OFFSET (GskQuadVertex, position));
- }
-
- if (uv_id != -1)
- {
- glEnableVertexAttribArray (uv_id);
- glVertexAttribPointer (uv_id, 2, GL_FLOAT, GL_FALSE,
- sizeof (GskQuadVertex),
- (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
- }
-
- glBindBuffer (GL_ARRAY_BUFFER, 0);
- glBindVertexArray (0);
-
- v = vao_new ();
- v->vao_id = vao_id;
- v->buffer_id = buffer_id;
- v->position_id = position_id;
- v->uv_id = uv_id;
- v->n_quads = n_vertices;
- v->quads = g_memdup (quads, sizeof (GskQuadVertex) * n_vertices);
- v->in_use = TRUE;
- g_hash_table_insert (driver->vaos, GINT_TO_POINTER (vao_id), v);
-
-#ifdef G_ENABLE_DEBUG
- if (GSK_DEBUG_CHECK (OPENGL))
- {
- int i;
- g_print ("New VAO(%d) for quad[%d] : {\n", v->vao_id, n_vertices);
- for (i = 0; i < n_vertices; i++)
- {
- g_print (" { x:%.2f, y:%.2f } { u:%.2f, v:%.2f }\n",
- quads[i].position[0], quads[i].position[1],
- quads[i].uv[0], quads[i].uv[1]);
- }
- g_print ("}\n");
- }
-#endif
-
- return vao_id;
-}
-
-int
-gsk_gl_driver_create_render_target (GskGLDriver *driver,
- int texture_id,
- gboolean add_depth_buffer,
- gboolean add_stencil_buffer)
-{
- GLuint fbo_id, depth_stencil_buffer_id;
- Texture *t;
- Fbo f;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), -1);
- g_return_val_if_fail (driver->in_frame, -1);
-
- t = gsk_gl_driver_get_texture (driver, texture_id);
- if (t == NULL)
- return -1;
-
- if (t->fbos == NULL)
- {
- t->fbos = g_array_new (FALSE, FALSE, sizeof (Fbo));
- g_array_set_clear_func (t->fbos, fbo_clear);
- }
-
- glGenFramebuffers (1, &fbo_id);
- glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, t->texture_id, 0);
-
- if (add_depth_buffer || add_stencil_buffer)
- glGenRenderbuffersEXT (1, &depth_stencil_buffer_id);
- else
- depth_stencil_buffer_id = 0;
-
- glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id);
-
- if (add_depth_buffer || add_stencil_buffer)
- {
- if (add_stencil_buffer)
- glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height);
- else
- glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height);
-
- if (add_depth_buffer)
- glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, depth_stencil_buffer_id);
-
- if (add_stencil_buffer)
- glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, depth_stencil_buffer_id);
- }
-
- f.fbo_id = fbo_id;
- f.depth_stencil_id = depth_stencil_buffer_id;
-
- g_array_append_val (t->fbos, f);
-
- g_assert_cmpint (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
- glBindFramebuffer (GL_FRAMEBUFFER, driver->default_fbo.fbo_id);
-
- return fbo_id;
-}
-
-void
-gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
- int texture_id)
-{
- Texture *t;
-
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
- g_return_if_fail (driver->in_frame);
-
- t = gsk_gl_driver_get_texture (driver, texture_id);
- if (t == NULL)
- {
- g_critical ("No texture %d found.", texture_id);
- return;
- }
-
- if (driver->bound_source_texture != t)
- {
- glActiveTexture (GL_TEXTURE0);
- glBindTexture (GL_TEXTURE_2D, t->texture_id);
-
- driver->bound_source_texture = t;
- }
-}
-
-void
-gsk_gl_driver_bind_mask_texture (GskGLDriver *driver,
- int texture_id)
-{
- Texture *t;
-
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
- g_return_if_fail (driver->in_frame);
-
- t = gsk_gl_driver_get_texture (driver, texture_id);
- if (t == NULL)
- {
- g_critical ("No texture %d found.", texture_id);
- return;
- }
-
- if (driver->bound_mask_texture != t)
- {
- glActiveTexture (GL_TEXTURE0 + 1);
- glBindTexture (GL_TEXTURE_2D, t->texture_id);
-
- glActiveTexture (GL_TEXTURE0);
-
- driver->bound_mask_texture = t;
- }
-}
-
-void
-gsk_gl_driver_bind_vao (GskGLDriver *driver,
- int vao_id)
-{
- Vao *v;
-
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
- g_return_if_fail (driver->in_frame);
-
- v = gsk_gl_driver_get_vao (driver, vao_id);
- if (v == NULL)
- {
- g_critical ("No VAO %d found.", vao_id);
- return;
- }
-
- if (driver->bound_vao != v)
- {
- glBindVertexArray (v->vao_id);
- glBindBuffer (GL_ARRAY_BUFFER, v->buffer_id);
-
- if (v->position_id != -1)
- glEnableVertexAttribArray (v->position_id);
-
- if (v->uv_id != -1)
- glEnableVertexAttribArray (v->uv_id);
-
- driver->bound_vao = v;
- }
-}
-
-gboolean
-gsk_gl_driver_bind_render_target (GskGLDriver *driver,
- int texture_id)
-{
- int status;
- Fbo *f;
-
- g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), FALSE);
- g_return_val_if_fail (driver->in_frame, FALSE);
-
- if (texture_id == 0)
- {
- glBindFramebuffer (GL_FRAMEBUFFER, 0);
- driver->bound_fbo = &driver->default_fbo;
- goto out;
- }
-
- f = gsk_gl_driver_get_fbo (driver, texture_id);
- if (f == NULL)
- {
- g_critical ("No render target associated to texture %d found.", texture_id);
- return FALSE;
- }
-
- if (f != driver->bound_fbo)
- {
- glBindFramebuffer (GL_FRAMEBUFFER, f->fbo_id);
-
- driver->bound_fbo = f;
- }
-
-out:
-
- if (texture_id != 0)
- {
- status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
- g_assert_cmpint (status, ==, GL_FRAMEBUFFER_COMPLETE);
- }
-
- return TRUE;
-}
-
-void
-gsk_gl_driver_destroy_texture (GskGLDriver *driver,
- int texture_id)
-{
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
-
- g_hash_table_remove (driver->textures, GINT_TO_POINTER (texture_id));
-}
-
-void
-gsk_gl_driver_destroy_vao (GskGLDriver *driver,
- int vao_id)
-{
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
-
- g_hash_table_remove (driver->vaos, GINT_TO_POINTER (vao_id));
-}
-
-static void
-gsk_gl_driver_set_texture_parameters (GskGLDriver *driver,
- int min_filter,
- int mag_filter)
-{
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
-void
-gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
- int texture_id)
-{
- Texture *t;
-
- g_return_if_fail (GSK_IS_GL_DRIVER (driver));
-
- t = gsk_gl_driver_get_texture (driver, texture_id);
- if (t == NULL)
- {
- g_critical ("No texture %d found.", texture_id);
- return;
- }
-
- if (!(driver->bound_source_texture == t || driver->bound_mask_texture == t))
- {
- g_critical ("You must bind the texture before initializing it.");
- return;
- }
-
- gsk_gl_driver_set_texture_parameters (driver, t->min_filter, t->mag_filter);
-
- if (gdk_gl_context_get_use_es (driver->gl_context))
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- else
- glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
-
- glBindTexture (GL_TEXTURE_2D, 0);
-}
-
-void
-gsk_gl_driver_init_texture_with_surface (GskGLDriver *self,
- int texture_id,
- cairo_surface_t *surface,
- int min_filter,
- int mag_filter)
-{
- Texture *t;
-
- g_return_if_fail (GSK_IS_GL_DRIVER (self));
-
- t = gsk_gl_driver_get_texture (self, texture_id);
- if (t == NULL)
- {
- g_critical ("No texture %d found.", texture_id);
- return;
- }
-
- if (!(self->bound_source_texture == t || self->bound_mask_texture == t))
- {
- g_critical ("You must bind the texture before initializing it.");
- return;
- }
-
- gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
-
- gdk_cairo_surface_upload_to_gl (surface, GL_TEXTURE_2D, t->width, t->height, NULL);
- gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
-
- t->min_filter = min_filter;
- t->mag_filter = mag_filter;
-
- if (t->min_filter != GL_NEAREST)
- glGenerateMipmap (GL_TEXTURE_2D);
-}
+++ /dev/null
-#ifndef __GSK_GL_DRIVER_PRIVATE_H__
-#define __GSK_GL_DRIVER_PRIVATE_H__
-
-#include <cairo.h>
-#include <gdk/gdk.h>
-#include <graphene.h>
-
-G_BEGIN_DECLS
-
-#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type ())
-
-G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject)
-
-typedef struct {
- float position[2];
- float uv[2];
-} GskQuadVertex;
-
-GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
-
-int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
-
-void gsk_gl_driver_begin_frame (GskGLDriver *driver);
-void gsk_gl_driver_end_frame (GskGLDriver *driver);
-
-int gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
- GdkTexture *texture,
- int min_filter,
- int mag_filter);
-int gsk_gl_driver_create_permanent_texture (GskGLDriver *driver,
- float width,
- float height);
-int gsk_gl_driver_create_texture (GskGLDriver *driver,
- float width,
- float height);
-int gsk_gl_driver_create_vao_for_quad (GskGLDriver *driver,
- int position_id,
- int uv_id,
- int n_vertices,
- GskQuadVertex *vertices);
-void gsk_gl_driver_create_permanent_vao_for_quad (GskGLDriver *driver,
- int n_vertices,
- const GskQuadVertex *quads,
- int *out_vao_id,
- int *out_vao_buffer_id);
-int gsk_gl_driver_create_render_target (GskGLDriver *driver,
- int texture_id,
- gboolean add_depth_buffer,
- gboolean add_stencil_buffer);
-
-void gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
- int texture_id);
-void gsk_gl_driver_bind_mask_texture (GskGLDriver *driver,
- int texture_id);
-void gsk_gl_driver_bind_vao (GskGLDriver *driver,
- int vao_id);
-gboolean gsk_gl_driver_bind_render_target (GskGLDriver *driver,
- int texture_id);
-
-void gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
- int texture_id);
-void gsk_gl_driver_init_texture_with_surface (GskGLDriver *driver,
- int texture_id,
- cairo_surface_t *surface,
- int min_filter,
- int mag_filter);
-
-void gsk_gl_driver_destroy_texture (GskGLDriver *driver,
- int texture_id);
-void gsk_gl_driver_destroy_vao (GskGLDriver *driver,
- int vao_id);
-
-int gsk_gl_driver_collect_textures (GskGLDriver *driver);
-int gsk_gl_driver_collect_vaos (GskGLDriver *driver);
-
-G_END_DECLS
-
-#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */
+++ /dev/null
-#include "config.h"
-
-#include "gskglglyphcacheprivate.h"
-#include "gskgldriverprivate.h"
-#include "gskdebugprivate.h"
-#include "gskprivate.h"
-
-#include <graphene.h>
-#include <cairo/cairo.h>
-#include <epoxy/gl.h>
-
-/* Parameters for our cache eviction strategy.
- *
- * Each cached glyph has an age that gets reset every time a cached glyph gets used.
- * Glyphs that have not been used for the MAX_AGE frames are considered old. We keep
- * count of the pixels of each atlas that are taken up by old glyphs. We check the
- * fraction of old pixels every CHECK_INTERVAL frames, and if it is above MAX_OLD, then
- * we drop the atlas an all the glyphs contained in it from the cache.
- */
-
-#define MAX_AGE 60
-#define CHECK_INTERVAL 10
-#define MAX_OLD 0.333
-
-#define ATLAS_SIZE 512
-
-typedef struct
-{
- PangoFont *font;
- PangoGlyph glyph;
- guint scale; /* times 1024 */
-} GlyphCacheKey;
-
-typedef struct
-{
- GlyphCacheKey *key;
- GskGLCachedGlyph *value;
- cairo_surface_t *surface;
-} DirtyGlyph;
-
-
-static guint glyph_cache_hash (gconstpointer v);
-static gboolean glyph_cache_equal (gconstpointer v1,
- gconstpointer v2);
-static void glyph_cache_key_free (gpointer v);
-static void glyph_cache_value_free (gpointer v);
-static void dirty_glyph_free (gpointer v);
-
-static GskGLGlyphAtlas *
-create_atlas (GskGLGlyphCache *cache)
-{
- GskGLGlyphAtlas *atlas;
-
- atlas = g_new0 (GskGLGlyphAtlas, 1);
- atlas->width = ATLAS_SIZE;
- atlas->height = ATLAS_SIZE;
- atlas->y0 = 1;
- atlas->y = 1;
- atlas->x = 1;
- atlas->image = NULL;
- atlas->num_glyphs = 0;
- atlas->dirty_glyphs = NULL;
-
- return atlas;
-}
-
-static void
-free_atlas (gpointer v)
-{
- GskGLGlyphAtlas *atlas = v;
-
- if (atlas->image)
- {
- g_assert (atlas->image->texture_id == 0);
- g_free (atlas->image);
- }
- g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
- g_free (atlas);
-}
-
-void
-gsk_gl_glyph_cache_init (GskGLGlyphCache *self,
- GskGLDriver *gl_driver)
-{
- self->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
- glyph_cache_key_free, glyph_cache_value_free);
- self->atlases = g_ptr_array_new_with_free_func (free_atlas);
- g_ptr_array_add (self->atlases, create_atlas (self));
-
- self->gl_driver = gl_driver;
-}
-
-void
-gsk_gl_glyph_cache_free (GskGLGlyphCache *self)
-{
- guint i;
-
- for (i = 0; i < self->atlases->len; i ++)
- {
- GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->image)
- {
- gsk_gl_image_destroy (atlas->image, self->gl_driver);
- atlas->image->texture_id = 0;
- }
- }
-
- g_ptr_array_unref (self->atlases);
- g_hash_table_unref (self->hash_table);
-}
-
-static gboolean
-glyph_cache_equal (gconstpointer v1, gconstpointer v2)
-{
- const GlyphCacheKey *key1 = v1;
- const GlyphCacheKey *key2 = v2;
-
- return key1->font == key2->font &&
- key1->glyph == key2->glyph &&
- key1->scale == key2->scale;
-}
-
-static guint
-glyph_cache_hash (gconstpointer v)
-{
- const GlyphCacheKey *key = v;
-
- return GPOINTER_TO_UINT (key->font) ^ key->glyph ^ key->scale;
-}
-
-static void
-glyph_cache_key_free (gpointer v)
-{
- GlyphCacheKey *f = v;
-
- g_object_unref (f->font);
- g_free (f);
-}
-
-static void
-glyph_cache_value_free (gpointer v)
-{
- g_free (v);
-}
-
-static void
-dirty_glyph_free (gpointer v)
-{
- DirtyGlyph *glyph = v;
-
- if (glyph->surface)
- cairo_surface_destroy (glyph->surface);
- g_free (glyph);
-}
-
-static void
-add_to_cache (GskGLGlyphCache *cache,
- GlyphCacheKey *key,
- GskGLCachedGlyph *value)
-{
- GskGLGlyphAtlas *atlas;
- int i;
- DirtyGlyph *dirty;
- int width = value->draw_width * key->scale / 1024;
- int height = value->draw_height * key->scale / 1024;
-
- for (i = 0; i < cache->atlases->len; i++)
- {
- int x, y, y0;
-
- atlas = g_ptr_array_index (cache->atlases, i);
- x = atlas->x;
- y = atlas->y;
- y0 = atlas->y0;
-
- if (atlas->x + width + 1 >= atlas->width)
- {
- /* start a new row */
- y0 = y + 1;
- x = 1;
- }
-
- if (y0 + height + 1 >= atlas->height)
- continue;
-
- atlas->y0 = y0;
- atlas->x = x;
- atlas->y = y;
- break;
- }
-
- if (i == cache->atlases->len)
- {
- atlas = create_atlas (cache);
- g_ptr_array_add (cache->atlases, atlas);
- }
-
- value->tx = (float)atlas->x / atlas->width;
- value->ty = (float)atlas->y0 / atlas->height;
- value->tw = (float)width / atlas->width;
- value->th = (float)height / atlas->height;
-
- value->atlas = atlas;
-
- dirty = g_new0 (DirtyGlyph, 1);
- dirty->key = key;
- dirty->value = value;
- atlas->dirty_glyphs = g_list_prepend (atlas->dirty_glyphs, dirty);
-
- atlas->x = atlas->x + width + 1;
- atlas->y = MAX (atlas->y, atlas->y0 + height + 1);
-
- atlas->num_glyphs++;
-
-#ifdef G_ENABLE_DEBUG
- if (GSK_DEBUG_CHECK(GLYPH_CACHE))
- {
- g_print ("Glyph cache:\n");
- for (i = 0; i < cache->atlases->len; i++)
- {
- atlas = g_ptr_array_index (cache->atlases, i);
- g_print ("\tGskGLGlyphAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
- i, atlas->width, atlas->height,
- atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
- 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
- atlas->x, atlas->y0, atlas->y);
- }
- }
-#endif
-}
-
-static void
-render_glyph (const GskGLGlyphAtlas *atlas,
- DirtyGlyph *glyph,
- GskImageRegion *region)
-{
- GlyphCacheKey *key = glyph->key;
- GskGLCachedGlyph *value = glyph->value;
- cairo_surface_t *surface;
- cairo_t *cr;
- cairo_scaled_font_t *scaled_font;
- cairo_glyph_t cg;
-
- scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->font);
- if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
- return;
-
- surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
- value->draw_width * key->scale / 1024,
- value->draw_height * key->scale / 1024);
- cairo_surface_set_device_scale (surface, key->scale / 1024.0, key->scale / 1024.0);
-
- cr = cairo_create (surface);
-
- cairo_set_scaled_font (cr, scaled_font);
- cairo_set_source_rgba (cr, 1, 1, 1, 1);
-
- cg.index = key->glyph;
- cg.x = - value->draw_x;
- cg.y = - value->draw_y;
-
- cairo_show_glyphs (cr, &cg, 1);
-
- cairo_destroy (cr);
-
- glyph->surface = surface;
-
- region->data = cairo_image_surface_get_data (surface);
- region->width = cairo_image_surface_get_width (surface);
- region->height = cairo_image_surface_get_height (surface);
- region->stride = cairo_image_surface_get_stride (surface);
- region->x = (gsize)(value->tx * atlas->width);
- region->y = (gsize)(value->ty * atlas->height);
-}
-
-static void
-upload_dirty_glyphs (GskGLGlyphCache *self,
- GskGLGlyphAtlas *atlas)
-{
- GList *l;
- guint num_regions;
- GskImageRegion *regions;
- int i;
-
- num_regions = g_list_length (atlas->dirty_glyphs);
- regions = alloca (sizeof (GskImageRegion) * num_regions);
-
- for (l = atlas->dirty_glyphs, i = 0; l; l = l->next, i++)
- render_glyph (atlas, (DirtyGlyph *)l->data, ®ions[i]);
-
- GSK_NOTE (GLYPH_CACHE,
- g_print ("uploading %d glyphs to cache\n", num_regions));
-
-
- gsk_gl_image_upload_regions (atlas->image, self->gl_driver, num_regions, regions);
-
- g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
- atlas->dirty_glyphs = NULL;
-}
-
-const GskGLCachedGlyph *
-gsk_gl_glyph_cache_lookup (GskGLGlyphCache *cache,
- gboolean create,
- PangoFont *font,
- PangoGlyph glyph,
- float scale)
-{
- GlyphCacheKey lookup_key;
- GskGLCachedGlyph *value;
-
- lookup_key.font = font;
- lookup_key.glyph = glyph;
- lookup_key.scale = (guint)(scale * 1024);
-
- value = g_hash_table_lookup (cache->hash_table, &lookup_key);
-
- if (value)
- {
- if (cache->timestamp - value->timestamp >= MAX_AGE)
- {
- GskGLGlyphAtlas *atlas = value->atlas;
-
- if (atlas)
- atlas->old_pixels -= value->draw_width * value->draw_height;
-
- value->timestamp = cache->timestamp;
- }
- }
-
- if (create && value == NULL)
- {
- GlyphCacheKey *key;
- PangoRectangle ink_rect;
-
- key = g_new0 (GlyphCacheKey, 1);
- value = g_new0 (GskGLCachedGlyph, 1);
-
- pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL);
- pango_extents_to_pixels (&ink_rect, NULL);
-
- value->draw_x = ink_rect.x;
- value->draw_y = ink_rect.y;
- value->draw_width = ink_rect.width;
- value->draw_height = ink_rect.height;
- value->timestamp = cache->timestamp;
- value->atlas = NULL; /* For now */
-
- key->font = g_object_ref (font);
- key->glyph = glyph;
- key->scale = (guint)(scale * 1024);
-
- if (ink_rect.width > 0 && ink_rect.height > 0)
- add_to_cache (cache, key, value);
-
- g_hash_table_insert (cache->hash_table, key, value);
- }
-
- return value;
-}
-
-GskGLImage *
-gsk_gl_glyph_cache_get_glyph_image (GskGLGlyphCache *self,
- const GskGLCachedGlyph *glyph)
-{
- GskGLGlyphAtlas *atlas = glyph->atlas;
-
- g_assert (atlas != NULL);
-
-
- if (atlas->image == NULL)
- {
- atlas->image = g_new0 (GskGLImage, 1);
- gsk_gl_image_create (atlas->image, self->gl_driver, atlas->width, atlas->height);
- }
-
- if (atlas->dirty_glyphs)
- upload_dirty_glyphs (self, atlas);
-
- return atlas->image;
-}
-
-void
-gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self)
-{
- int i;
- GHashTableIter iter;
- GlyphCacheKey *key;
- GskGLCachedGlyph *value;
- guint dropped = 0;
-
- self->timestamp++;
-
-
- if (self->timestamp % CHECK_INTERVAL != 0)
- return;
-
- /* look for glyphs that have grown old since last time */
- g_hash_table_iter_init (&iter, self->hash_table);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
- {
- guint age;
-
- age = self->timestamp - value->timestamp;
- if (MAX_AGE <= age && age < MAX_AGE + CHECK_INTERVAL)
- {
- GskGLGlyphAtlas *atlas = value->atlas;
-
- if (atlas)
- atlas->old_pixels += value->draw_width * value->draw_height;
- }
- }
-
- /* look for atlases to drop, and create a mapping of updated texture indices */
- for (i = self->atlases->len - 1; i >= 0; i--)
- {
- GskGLGlyphAtlas *atlas = g_ptr_array_index (self->atlases, i);
-
- if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
- {
- GSK_NOTE(GLYPH_CACHE,
- g_print ("Dropping atlas %d (%g.2%% old)\n",
- i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height)));
-
- if (atlas->image)
- {
- gsk_gl_image_destroy (atlas->image, self->gl_driver);
- atlas->image->texture_id = 0;
- }
-
- /* Remove all glyphs that point to this atlas */
- g_hash_table_iter_init (&iter, self->hash_table);
- while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
- {
- if (value->atlas == atlas)
- g_hash_table_iter_remove (&iter);
- }
- /* TODO: The above loop inside this other loop could be slow... */
-
- g_ptr_array_remove_index (self->atlases, i);
- }
- }
-
- GSK_NOTE(GLYPH_CACHE, g_print ("Dropped %d glyphs\n", dropped));
-}
+++ /dev/null
-#ifndef __GSK_GL_GLYPH_CACHE_PRIVATE_H__
-#define __GSK_GL_GLYPH_CACHE_PRIVATE_H__
-
-#include "gskgldriverprivate.h"
-#include "gskglimageprivate.h"
-#include <pango/pango.h>
-#include <gdk/gdk.h>
-
-typedef struct
-{
- GskGLDriver *gl_driver;
-
- GHashTable *hash_table;
- GPtrArray *atlases;
-
- guint64 timestamp;
-} GskGLGlyphCache;
-
-
-typedef struct
-{
- GskGLImage *image;
- int width, height;
- int x, y, y0;
- int num_glyphs;
- GList *dirty_glyphs;
- guint old_pixels;
-} GskGLGlyphAtlas;
-
-typedef struct
-{
- GskGLGlyphAtlas *atlas;
-
- float tx;
- float ty;
- float tw;
- float th;
-
- int draw_x;
- int draw_y;
- int draw_width;
- int draw_height;
-
- guint64 timestamp;
-} GskGLCachedGlyph;
-
-void gsk_gl_glyph_cache_init (GskGLGlyphCache *self,
- GskGLDriver *gl_driver);
-void gsk_gl_glyph_cache_free (GskGLGlyphCache *self);
-void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self);
-GskGLImage * gsk_gl_glyph_cache_get_glyph_image (GskGLGlyphCache *self,
- const GskGLCachedGlyph *glyph);
-const GskGLCachedGlyph * gsk_gl_glyph_cache_lookup (GskGLGlyphCache *self,
- gboolean create,
- PangoFont *font,
- PangoGlyph glyph,
- float scale);
-
-#endif
+++ /dev/null
-
-#include "gskglimageprivate.h"
-#include <epoxy/gl.h>
-
-void
-gsk_gl_image_create (GskGLImage *self,
- GskGLDriver *gl_driver,
- int width,
- int height)
-{
- self->texture_id = gsk_gl_driver_create_permanent_texture (gl_driver, width, height);
- self->width = width;
- self->height = height;
-
- gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
- gsk_gl_driver_init_texture_empty (gl_driver, self->texture_id);
-}
-
-void
-gsk_gl_image_destroy (GskGLImage *self,
- GskGLDriver *gl_driver)
-{
- gsk_gl_driver_destroy_texture (gl_driver, self->texture_id);
-}
-
-void
-gsk_gl_image_write_to_png (const GskGLImage *self,
- GskGLDriver *gl_driver,
- const char *filename)
-{
- int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, self->width);
- guchar *data = g_malloc (self->height * stride);
- cairo_surface_t *s;
-
- gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
- glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
- s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, self->width, self->height, stride);
- cairo_surface_write_to_png (s, filename);
-
- cairo_surface_destroy (s);
- g_free (data);
-}
-
-void
-gsk_gl_image_upload_regions (GskGLImage *self,
- GskGLDriver *gl_driver,
- guint n_regions,
- const GskImageRegion *regions)
-{
- guint i;
-
- for (i = 0; i < n_regions; i ++)
- {
- const GskImageRegion *region = ®ions[i];
-
- gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
- glBindTexture (GL_TEXTURE_2D, self->texture_id);
-
- glTexSubImage2D (GL_TEXTURE_2D, 0, region->x, region->y, region->width, region->height,
- GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data);
- }
-
-#ifdef G_ENABLE_DEBUG
- /*gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);*/
- /*gsk_gl_image_dump (self, gl_driver, "/home/baedert/atlases/test_dump.png");*/
-#endif
-}
-
+++ /dev/null
-#ifndef __GSK_GL_IMAGE_H__
-#define __GSK_GL_IMAGE_H__
-
-#include "gskgldriverprivate.h"
-#include <cairo/cairo.h>
-
-typedef struct
-{
- guint texture_id;
- int width;
- int height;
-} GskGLImage;
-
-typedef struct
-{
- guchar *data;
- gsize width;
- gsize height;
- gsize stride;
- gsize x;
- gsize y;
-} GskImageRegion;
-
-void gsk_gl_image_create (GskGLImage *self,
- GskGLDriver *gl_driver,
- int width,
- int height);
-void gsk_gl_image_destroy (GskGLImage *self,
- GskGLDriver *gl_driver);
-void gsk_gl_image_write_to_png (const GskGLImage *self,
- GskGLDriver *gl_driver,
- const char *filename);
-void gsk_gl_image_upload_regions (GskGLImage *self,
- GskGLDriver *gl_driver,
- guint n_regions,
- const GskImageRegion *regions);
-
-
-#endif
-
+++ /dev/null
-#include "config.h"
-
-#include "gskglprofilerprivate.h"
-
-#include <epoxy/gl.h>
-
-#define N_QUERIES 4
-
-struct _GskGLProfiler
-{
- GObject parent_instance;
-
- GdkGLContext *gl_context;
-
- /* Creating GL queries is kind of expensive, so we pay the
- * price upfront and create a circular buffer of queries
- */
- GLuint gl_queries[N_QUERIES];
- GLuint active_query;
-
- gboolean has_timer : 1;
- gboolean first_frame : 1;
-};
-
-enum {
- PROP_GL_CONTEXT = 1,
-
- N_PROPERTIES
-};
-
-static GParamSpec *gsk_gl_profiler_properties[N_PROPERTIES];
-
-G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT)
-
-static void
-gsk_gl_profiler_finalize (GObject *gobject)
-{
- GskGLProfiler *self = GSK_GL_PROFILER (gobject);
-
- glDeleteQueries (N_QUERIES, self->gl_queries);
-
- g_clear_object (&self->gl_context);
-
- G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject);
-}
-
-static void
-gsk_gl_profiler_set_property (GObject *gobject,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GskGLProfiler *self = GSK_GL_PROFILER (gobject);
-
- switch (prop_id)
- {
- case PROP_GL_CONTEXT:
- self->gl_context = g_value_dup_object (value);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
- }
-}
-
-static void
-gsk_gl_profiler_get_property (GObject *gobject,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GskGLProfiler *self = GSK_GL_PROFILER (gobject);
-
- switch (prop_id)
- {
- case PROP_GL_CONTEXT:
- g_value_set_object (value, self->gl_context);
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
- }
-}
-
-static void
-gsk_gl_profiler_class_init (GskGLProfilerClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-
- gobject_class->set_property = gsk_gl_profiler_set_property;
- gobject_class->get_property = gsk_gl_profiler_get_property;
- gobject_class->finalize = gsk_gl_profiler_finalize;
-
- gsk_gl_profiler_properties[PROP_GL_CONTEXT] =
- g_param_spec_object ("gl-context",
- "GL Context",
- "The GdkGLContext used by the GL profiler",
- GDK_TYPE_GL_CONTEXT,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (gobject_class, N_PROPERTIES, gsk_gl_profiler_properties);
-}
-
-static void
-gsk_gl_profiler_init (GskGLProfiler *self)
-{
- glGenQueries (N_QUERIES, self->gl_queries);
-
- self->first_frame = TRUE;
- self->has_timer = epoxy_has_gl_extension ("GL_ARB_timer_query");
-}
-
-GskGLProfiler *
-gsk_gl_profiler_new (GdkGLContext *context)
-{
- g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
-
- return g_object_new (GSK_TYPE_GL_PROFILER, "gl-context", context, NULL);
-}
-
-void
-gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler)
-{
- GLuint query_id;
-
- g_return_if_fail (GSK_IS_GL_PROFILER (profiler));
-
- if (!profiler->has_timer)
- return;
-
- query_id = profiler->gl_queries[profiler->active_query];
- glBeginQuery (GL_TIME_ELAPSED, query_id);
-}
-
-guint64
-gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler)
-{
- GLuint last_query_id;
- GLint res;
- GLuint64 elapsed;
-
- g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0);
-
- if (!profiler->has_timer)
- return 0;
-
- glEndQuery (GL_TIME_ELAPSED);
-
- if (profiler->active_query == 0)
- last_query_id = N_QUERIES - 1;
- else
- last_query_id = profiler->active_query - 1;
-
- /* Advance iterator */
- profiler->active_query += 1;
- if (profiler->active_query == N_QUERIES)
- profiler->active_query = 0;
-
- /* If this is the first frame we already have a result */
- if (profiler->first_frame)
- {
- profiler->first_frame = FALSE;
- return 0;
- }
-
- glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res);
- if (res == 1)
- glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed);
- else
- elapsed = 0;
-
- return elapsed;
-}
+++ /dev/null
-#ifndef __GSK_GL_PROFILER_PRIVATE_H__
-#define __GSK_GL_PROFILER_PRIVATE_H__
-
-#include <gsk/gsktypes.h>
-
-G_BEGIN_DECLS
-
-#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ())
-G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject)
-
-GskGLProfiler * gsk_gl_profiler_new (GdkGLContext *context);
-
-void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler);
-guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler);
-
-G_END_DECLS
-
-#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */
+++ /dev/null
-#include "config.h"
-
-#include "gskglrendererprivate.h"
-
-#include "gskdebugprivate.h"
-#include "gskenums.h"
-#include "gskgldriverprivate.h"
-#include "gskglprofilerprivate.h"
-#include "gskprofilerprivate.h"
-#include "gskrendererprivate.h"
-#include "gskrendernodeprivate.h"
-#include "gskshaderbuilderprivate.h"
-#include "gskglglyphcacheprivate.h"
-#include "gdk/gdktextureprivate.h"
-
-#include "gskprivate.h"
-
-#include <epoxy/gl.h>
-#include <cairo-ft.h>
-
-#define SHADER_VERSION_GLES 100
-#define SHADER_VERSION_GL2_LEGACY 110
-#define SHADER_VERSION_GL3_LEGACY 130
-#define SHADER_VERSION_GL3 150
-
-#define ORTHO_NEAR_PLANE -10000
-#define ORTHO_FAR_PLANE 10000
-
-#define N_VERTICES 6
-#define N_PROGRAMS 6
-
-#define HIGHLIGHT_FALLBACK 0
-#define DEBUG_OPS 0
-
-#if DEBUG_OPS
-#define OP_PRINT(format, ...) g_print(format, ## __VA_ARGS__)
-#else
-#define OP_PRINT(format, ...)
-#endif
-
-#define INIT_PROGRAM_UNIFORM_LOCATION(program_name, location_name, uniform_name) \
- G_STMT_START{\
- self->program_name.location_name = glGetUniformLocation(self->program_name.id, uniform_name);\
- g_assert (self->program_name.location_name != 0); \
- }G_STMT_END
-
-
-
-static void G_GNUC_UNUSED
-dump_framebuffer (const char *filename, int w, int h)
-{
- int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w);
- guchar *data = g_malloc (h * stride);
- cairo_surface_t *s;
-
- glReadPixels (0, 0, w, h, GL_BGRA, GL_UNSIGNED_BYTE, data);
- s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, w, h, stride);
- cairo_surface_write_to_png (s, filename);
-
- cairo_surface_destroy (s);
- g_free (data);
-}
-
-static gboolean G_GNUC_UNUSED
-font_has_color_glyphs (const PangoFont *font)
-{
- cairo_scaled_font_t *scaled_font;
- gboolean has_color = FALSE;
-
- scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)font);
- if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
- {
- FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
- has_color = (FT_HAS_COLOR (ft_face) != 0);
- cairo_ft_scaled_font_unlock_face (scaled_font);
- }
-
- return has_color;
-}
-
-static void
-gsk_gl_renderer_setup_render_mode (GskGLRenderer *self);
-
-typedef struct
-{
- int index; /* Into the renderer's program array */
- const char *name; /* For debugging */
-
- int id;
- /* Common locations (gl_common)*/
- int source_location;
- int mask_location;
- int uv_location;
- int position_location;
- int alpha_location;
- int blend_mode_location;
- int viewport_location;
- int projection_location;
- int modelview_location;
- int clip_location;
- int clip_corner_widths_location;
- int clip_corner_heights_location;
-
- /* Program-specific locations */
- union {
- struct {
- int color_location;
- };
- struct {
- int color_matrix_location;
- int color_offset_location;
- };
- struct {
- int n_color_stops_location;
- int color_stops_location;
- int color_offsets_location;
- int start_point_location;
- int end_point_location;
- };
- struct {
- int clip_bounds_location;
- int corner_widths_location;
- int corner_heights_location;
- };
- };
-} Program;
-
-enum {
- MODE_BLIT = 1,
- MODE_COLOR,
- MODE_COLORING,
- MODE_TEXTURE,
- MODE_COLOR_MATRIX,
- MODE_LINEAR_GRADIENT,
- N_MODES
-};
-
-enum {
- OP_NONE,
- OP_CHANGE_OPACITY = 1,
- OP_CHANGE_COLOR = 2,
- OP_CHANGE_PROJECTION = 3,
- OP_CHANGE_MODELVIEW = 4,
- OP_CHANGE_PROGRAM = 5,
- OP_CHANGE_RENDER_TARGET = 6,
- OP_CHANGE_CLIP = 7,
- OP_CHANGE_VIEWPORT = 8,
- OP_CHANGE_SOURCE_TEXTURE = 9,
- OP_CHANGE_VAO = 10,
- OP_CHANGE_LINEAR_GRADIENT = 11,
- OP_DRAW = 12,
-};
-
-typedef struct
-{
- guint op;
-
- union {
- float opacity;
- graphene_matrix_t modelview; // TODO: Make both matrix members just "matrix".
- graphene_matrix_t projection;
- const Program *program;
- GdkRGBA color;
- gsize vao_offset;
- GskQuadVertex vertex_data[N_VERTICES]; // New Quad
- int texture_id;
- int render_target_id;
- GskRoundedRect clip;
- graphene_rect_t viewport;
- struct {
- int n_color_stops;
- float color_offsets[8];
- float color_stops[4 * 8];
- graphene_point_t start_point;
- graphene_point_t end_point;
- } linear_gradient;
- };
-} RenderOp;
-
-#ifdef G_ENABLE_DEBUG
-typedef struct
-{
- GQuark frames;
- GQuark draw_calls;
-} ProfileCounters;
-
-typedef struct
-{
- GQuark cpu_time;
- GQuark gpu_time;
-} ProfileTimers;
-#endif
-
-
-typedef enum
-{
- RENDER_FULL,
- RENDER_SCISSOR
-} RenderMode;
-
-struct _GskGLRenderer
-{
- GskRenderer parent_instance;
-
- int scale_factor;
-
- graphene_rect_t viewport;
-
- guint frame_buffer;
- guint depth_stencil_buffer;
- guint texture_id;
-
-
- GdkGLContext *gl_context;
- GskGLDriver *gl_driver;
- GskGLProfiler *gl_profiler;
-
- union {
- Program programs[N_PROGRAMS];
- struct {
- Program blend_program;
- Program blit_program;
- Program color_program;
- Program coloring_program;
- Program color_matrix_program;
- Program linear_gradient_program;
- };
- };
-
- GArray *render_ops;
-
- GskGLGlyphCache glyph_cache;
- int full_vao_id;
- int full_vao_buffer_id;
-
-#ifdef G_ENABLE_DEBUG
- ProfileCounters profile_counters;
- ProfileTimers profile_timers;
-#endif
-
- RenderMode render_mode;
-
- gboolean has_buffers : 1;
-};
-
-struct _GskGLRendererClass
-{
- GskRendererClass parent_class;
-};
-
-G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
-
-typedef struct
-{
- /* Per-Program State */
- struct {
- GskRoundedRect clip;
- graphene_matrix_t modelview;
- graphene_matrix_t projection;
- int source_texture;
- graphene_rect_t viewport;
- } program_state[N_PROGRAMS];
-
- /* Current global state */
- const Program *current_program;
- int current_render_target;
- int current_vao;
- int current_texture;
- GskRoundedRect current_clip;
- graphene_matrix_t current_modelview;
- graphene_matrix_t current_projection;
- graphene_rect_t current_viewport;
- float current_opacity;
-
- gsize buffer_size;
-
- GskGLRenderer *renderer;
-} RenderOpBuilder;
-
-static void
-add_program_op (RenderOpBuilder *builder,
- const Program *new_program)
-{
- static const GskRoundedRect empty_clip;
- static const graphene_matrix_t empty_matrix;
- static const graphene_rect_t empty_rect;
- RenderOp op;
-
- if (builder->current_program == new_program)
- return;
-
- op.op = OP_CHANGE_PROGRAM;
- op.program = new_program;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->current_program = new_program;
-
- /* If the projection is not yet set for this program, we use the current one. */
- if (memcmp (&empty_matrix, &builder->program_state[new_program->index].projection, sizeof (graphene_matrix_t)) == 0 ||
- memcmp (&builder->current_projection, &builder->program_state[new_program->index].projection, sizeof (graphene_matrix_t)) != 0)
- {
- op.op = OP_CHANGE_PROJECTION;
- op.projection = builder->current_projection;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->program_state[new_program->index].projection = builder->current_projection;
- }
-
- if (memcmp (&empty_matrix, &builder->program_state[new_program->index].modelview, sizeof (graphene_matrix_t)) == 0 ||
- memcmp (&builder->current_modelview, &builder->program_state[new_program->index].modelview, sizeof (graphene_matrix_t)) != 0)
- {
- op.op = OP_CHANGE_MODELVIEW;
- op.modelview = builder->current_modelview;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->program_state[new_program->index].modelview = builder->current_modelview;
- }
-
- if (memcmp (&empty_rect, &builder->program_state[new_program->index].viewport, sizeof (graphene_rect_t)) == 0 ||
- memcmp (&builder->current_viewport, &builder->program_state[new_program->index].viewport, sizeof (graphene_rect_t)) != 0)
- {
- op.op = OP_CHANGE_VIEWPORT;
- op.viewport = builder->current_viewport;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->program_state[new_program->index].viewport = builder->current_viewport;
- }
-
- if (memcmp (&empty_clip, &builder->program_state[new_program->index].clip, sizeof (GskRoundedRect)) == 0 ||
- memcmp (&builder->current_clip, &builder->program_state[new_program->index].clip, sizeof (GskRoundedRect)) != 0)
- {
- op.op = OP_CHANGE_CLIP;
- op.clip = builder->current_clip;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->program_state[new_program->index].clip = builder->current_clip;
- }
-
- if (graphene_rect_equal (&empty_rect, &builder->program_state[new_program->index].viewport) ||
- !graphene_rect_equal (&builder->current_viewport, &builder->program_state[new_program->index].viewport))
- {
- op.op = OP_CHANGE_VIEWPORT;
- op.viewport = builder->current_viewport;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->program_state[new_program->index].viewport = builder->current_viewport;
- }
-}
-
-static GskRoundedRect
-add_clip_op (RenderOpBuilder *builder,
- const GskRoundedRect *new_clip)
-{
- RenderOp op;
- GskRoundedRect prev_clip;
-
- op.op = OP_CHANGE_CLIP;
- op.clip = *new_clip;
- g_array_append_val (builder->renderer->render_ops, op);
-
- if (builder->current_program != NULL)
- builder->program_state[builder->current_program->index].clip = *new_clip;
-
- prev_clip = builder->current_clip;
- builder->current_clip = *new_clip;
-
- return prev_clip;
-}
-
-static graphene_matrix_t
-add_modelview_op (RenderOpBuilder *builder,
- const graphene_matrix_t *matrix)
-{
- RenderOp op;
- graphene_matrix_t prev_mv;
- RenderOp *last_op;
-
- last_op = &g_array_index (builder->renderer->render_ops, RenderOp, builder->renderer->render_ops->len - 1);
- if (last_op->op == OP_CHANGE_MODELVIEW)
- {
- last_op->modelview = *matrix;
- }
- else
- {
- op.op = OP_CHANGE_MODELVIEW;
- op.modelview = *matrix;
- g_array_append_val (builder->renderer->render_ops, op);
- }
-
- if (builder->current_program != NULL)
- builder->program_state[builder->current_program->index].modelview = *matrix;
-
- prev_mv = builder->current_modelview;
- builder->current_modelview = *matrix;
-
- return prev_mv;
-}
-
-static graphene_matrix_t
-add_projection_op (RenderOpBuilder *builder,
- const graphene_matrix_t *matrix)
-{
- RenderOp op;
- graphene_matrix_t prev_proj;
-
- op.op = OP_CHANGE_PROJECTION;
- op.projection = *matrix;
- g_array_append_val (builder->renderer->render_ops, op);
-
- if (builder->current_program != NULL)
- builder->program_state[builder->current_program->index].projection = *matrix;
-
- prev_proj = builder->current_projection;
- builder->current_projection = *matrix;
-
- return prev_proj;
-}
-
-static graphene_rect_t
-add_viewport_op (RenderOpBuilder *builder,
- const graphene_rect_t *viewport)
-{
- RenderOp op;
- graphene_rect_t prev_viewport;
-
- op.op = OP_CHANGE_VIEWPORT;
- op.viewport = *viewport;
- g_array_append_val (builder->renderer->render_ops, op);
-
- if (builder->current_program != NULL)
- builder->program_state[builder->current_program->index].viewport = *viewport;
-
- prev_viewport = builder->current_viewport;
- builder->current_viewport = *viewport;
-
- return prev_viewport;
-}
-
-static void
-add_texture_op (RenderOpBuilder *builder,
- int texture_id)
-{
- RenderOp op;
-
- if (builder->current_texture == texture_id)
- return;
-
- op.op = OP_CHANGE_SOURCE_TEXTURE;
- op.texture_id = texture_id;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->current_texture = texture_id;
-}
-
-static float
-add_opacity_op (RenderOpBuilder *builder,
- float opacity)
-{
- RenderOp op;
- float prev_opacity;
-
- if (builder->current_opacity == opacity)
- return opacity;
-
- op.op = OP_CHANGE_OPACITY;
- op.opacity = opacity;
- g_array_append_val (builder->renderer->render_ops, op);
-
- prev_opacity = builder->current_opacity;
- builder->current_opacity = opacity;
-
- return prev_opacity;
-}
-
-static int
-add_render_target_op (RenderOpBuilder *builder,
- int render_target_id)
-{
- RenderOp op;
- int prev_render_target;
-
- if (builder->current_render_target == render_target_id)
- return render_target_id;
-
- prev_render_target = builder->current_render_target;
- op.op = OP_CHANGE_RENDER_TARGET;
- op.render_target_id = render_target_id;
- g_array_append_val (builder->renderer->render_ops, op);
- builder->current_render_target = render_target_id;
-
- return prev_render_target;
-}
-
-static void
-add_draw_op (RenderOpBuilder *builder,
- const GskQuadVertex vertex_data[N_VERTICES])
-{
- RenderOp op;
- gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
-
- op.op = OP_CHANGE_VAO;
- memcpy (&op.vertex_data, vertex_data, sizeof(GskQuadVertex) * N_VERTICES);
- g_array_append_val (builder->renderer->render_ops, op);
- builder->buffer_size += sizeof (GskQuadVertex) * N_VERTICES;
-
- op.op = OP_DRAW;
- op.vao_offset = offset;
- g_array_append_val (builder->renderer->render_ops, op);
-}
-
-static void
-add_op (RenderOpBuilder *builder,
- const RenderOp *op)
-{
- g_array_append_val (builder->renderer->render_ops, *op);
-}
-
-static void
-gsk_gl_renderer_dispose (GObject *gobject)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (gobject);
-
- g_clear_pointer (&self->render_ops, g_array_unref);
-
- G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
-}
-
-static void
-gsk_gl_renderer_create_buffers (GskGLRenderer *self,
- int width,
- int height,
- int scale_factor)
-{
- if (self->has_buffers)
- return;
-
- GSK_NOTE (OPENGL, g_print ("Creating buffers (w:%d, h:%d, scale:%d)\n", width, height, scale_factor));
-
- if (self->texture_id == 0)
- {
- self->texture_id = gsk_gl_driver_create_texture (self->gl_driver,
- width * scale_factor,
- height * scale_factor);
- gsk_gl_driver_bind_source_texture (self->gl_driver, self->texture_id);
- gsk_gl_driver_init_texture_empty (self->gl_driver, self->texture_id);
- }
-
- gsk_gl_driver_create_render_target (self->gl_driver, self->texture_id, TRUE, TRUE);
- gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id);
-
- self->has_buffers = TRUE;
-}
-
-static void
-gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
-{
- if (self->gl_context == NULL)
- return;
-
- if (!self->has_buffers)
- return;
-
- GSK_NOTE (OPENGL, g_print ("Destroying buffers\n"));
-
- gdk_gl_context_make_current (self->gl_context);
-
- if (self->texture_id != 0)
- {
- gsk_gl_driver_destroy_texture (self->gl_driver, self->texture_id);
- self->texture_id = 0;
- }
-
- self->has_buffers = FALSE;
-}
-
-static void
-init_common_locations (GskGLRenderer *self,
- GskShaderBuilder *builder,
- Program *prog)
-{
- prog->source_location = glGetUniformLocation (prog->id, "uSource");
- prog->mask_location = glGetUniformLocation (prog->id, "uMask");
- prog->alpha_location = glGetUniformLocation (prog->id, "uAlpha");
- prog->blend_mode_location = glGetUniformLocation (prog->id, "uBlendMode");
- prog->viewport_location = glGetUniformLocation (prog->id, "uViewport");
- prog->projection_location = glGetUniformLocation (prog->id, "uProjection");
- prog->modelview_location = glGetUniformLocation (prog->id, "uModelview");
- prog->clip_location = glGetUniformLocation (prog->id, "uClip");
- prog->clip_corner_widths_location = glGetUniformLocation (prog->id, "uClipCornerWidths");
- prog->clip_corner_heights_location = glGetUniformLocation (prog->id, "uClipCornerHeights");
-
- prog->position_location = glGetAttribLocation (prog->id, "aPosition");
- prog->uv_location = glGetAttribLocation (prog->id, "aUv");
-}
-
-static gboolean
-gsk_gl_renderer_create_programs (GskGLRenderer *self,
- GError **error)
-{
- GskShaderBuilder *builder;
- GError *shader_error = NULL;
- gboolean res = FALSE;
-
- builder = gsk_shader_builder_new ();
-
- gsk_shader_builder_set_resource_base_path (builder, "/org/gtk/libgsk/glsl");
-
- if (gdk_gl_context_get_use_es (self->gl_context))
- {
- gsk_shader_builder_set_version (builder, SHADER_VERSION_GLES);
- gsk_shader_builder_set_vertex_preamble (builder, "es2_common.vs.glsl");
- gsk_shader_builder_set_fragment_preamble (builder, "es2_common.fs.glsl");
- gsk_shader_builder_add_define (builder, "GSK_GLES", "1");
- }
- else if (gdk_gl_context_is_legacy (self->gl_context))
- {
- int maj, min;
- gdk_gl_context_get_version (self->gl_context, &maj, &min);
-
- if (maj == 3)
- gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3_LEGACY);
- else
- gsk_shader_builder_set_version (builder, SHADER_VERSION_GL2_LEGACY);
-
- gsk_shader_builder_set_vertex_preamble (builder, "gl_common.vs.glsl");
- gsk_shader_builder_set_fragment_preamble (builder, "gl_common.fs.glsl");
- gsk_shader_builder_add_define (builder, "GSK_LEGACY", "1");
- }
- else
- {
- gsk_shader_builder_set_version (builder, SHADER_VERSION_GL3);
- gsk_shader_builder_set_vertex_preamble (builder, "gl3_common.vs.glsl");
- gsk_shader_builder_set_fragment_preamble (builder, "gl3_common.fs.glsl");
- gsk_shader_builder_add_define (builder, "GSK_GL3", "1");
- }
-
-#ifdef G_ENABLE_DEBUG
- if (GSK_RENDER_MODE_CHECK (SHADERS))
- gsk_shader_builder_add_define (builder, "GSK_DEBUG", "1");
-#endif
-
- self->blend_program.id = gsk_shader_builder_create_program (builder,
- "blend.vs.glsl", "blend.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'blend' program: ");
- goto out;
- }
- self->blend_program.index = 0;
- init_common_locations (self, builder, &self->blend_program);
-
- self->blit_program.id = gsk_shader_builder_create_program (builder,
- "blit.vs.glsl", "blit.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'blit' program: ");
- goto out;
- }
- self->blit_program.index = 1;
- init_common_locations (self, builder, &self->blit_program);
-
- self->color_program.id = gsk_shader_builder_create_program (builder,
- "blit.vs.glsl", "color.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'color' program: ");
- goto out;
- }
- self->color_program.index = 2;
- init_common_locations (self, builder, &self->color_program);
- INIT_PROGRAM_UNIFORM_LOCATION (color_program, color_location, "uColor");
-
- self->coloring_program.id = gsk_shader_builder_create_program (builder,
- "blit.vs.glsl", "coloring.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'coloring' program: ");
- goto out;
- }
- self->coloring_program.index = 3;
- init_common_locations (self, builder, &self->coloring_program);
- INIT_PROGRAM_UNIFORM_LOCATION (coloring_program, color_location, "uColor");
-
- self->color_matrix_program.id = gsk_shader_builder_create_program (builder,
- "blit.vs.glsl", "color_matrix.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'color_matrix' program: ");
- goto out;
- }
- self->color_matrix_program.index = 4;
- init_common_locations (self, builder, &self->color_matrix_program);
- INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_matrix_location, "uColorMatrix");
- INIT_PROGRAM_UNIFORM_LOCATION (color_matrix_program, color_offset_location, "uColorOffset");
-
- self->linear_gradient_program.id = gsk_shader_builder_create_program (builder,
- "blit.vs.glsl", "linear_gradient.fs.glsl",
- &shader_error);
- if (shader_error != NULL)
- {
- g_propagate_prefixed_error (error,
- shader_error,
- "Unable to create 'linear_gradient' program: ");
- goto out;
- }
- self->linear_gradient_program.index = 5;
- init_common_locations (self, builder, &self->linear_gradient_program);
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_stops_location, "uColorStops");
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, color_offsets_location, "uColorOffsets");
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, n_color_stops_location, "uNumColorStops");
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, start_point_location, "uStartPoint");
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, end_point_location, "uEndPoint");
-
- res = TRUE;
-
-out:
-
- g_object_unref (builder);
- return res;
-}
-
-static gboolean
-gsk_gl_renderer_realize (GskRenderer *renderer,
- GdkWindow *window,
- GError **error)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
- GskQuadVertex vertex_data[N_VERTICES] = {
- { { 0, 0 }, { 0, 0 }, },
- { { 0, 1 }, { 0, 1 }, },
- { { 1, 0 }, { 1, 0 }, },
-
- { { 1, 1 }, { 1, 1 }, },
- { { 0, 1 }, { 0, 1 }, },
- { { 1, 0 }, { 1, 0 }, },
- };
-
-
- self->scale_factor = gdk_window_get_scale_factor (window);
-
- /* If we didn't get a GdkGLContext before realization, try creating
- * one now, for our exclusive use.
- */
- if (self->gl_context == NULL)
- {
- self->gl_context = gdk_window_create_gl_context (window, error);
- if (self->gl_context == NULL)
- return FALSE;
- }
-
- if (!gdk_gl_context_realize (self->gl_context, error))
- return FALSE;
-
- gdk_gl_context_make_current (self->gl_context);
-
- g_assert (self->gl_driver == NULL);
- self->gl_profiler = gsk_gl_profiler_new (self->gl_context);
- self->gl_driver = gsk_gl_driver_new (self->gl_context);
-
- GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
- if (!gsk_gl_renderer_create_programs (self, error))
- return FALSE;
-
- gsk_gl_glyph_cache_init (&self->glyph_cache, self->gl_driver);
-
- gsk_gl_driver_create_permanent_vao_for_quad (self->gl_driver, N_VERTICES, vertex_data,
- &self->full_vao_id, &self->full_vao_buffer_id);
-
- return TRUE;
-}
-
-static void
-gsk_gl_renderer_unrealize (GskRenderer *renderer)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
-
- if (self->gl_context == NULL)
- return;
-
- gdk_gl_context_make_current (self->gl_context);
-
- /* We don't need to iterate to destroy the associated GL resources,
- * as they will be dropped when we finalize the GskGLDriver
- */
- g_array_set_size (self->render_ops, 0);
-
-
- glDeleteProgram (self->blend_program.id);
- glDeleteProgram (self->blit_program.id);
- glDeleteProgram (self->color_program.id);
- glDeleteProgram (self->coloring_program.id);
- glDeleteProgram (self->color_matrix_program.id);
- glDeleteProgram (self->linear_gradient_program.id);
-
- gsk_gl_renderer_destroy_buffers (self);
-
- gsk_gl_glyph_cache_free (&self->glyph_cache);
-
- g_clear_object (&self->gl_profiler);
- g_clear_object (&self->gl_driver);
-
- if (self->gl_context == gdk_gl_context_get_current ())
- gdk_gl_context_clear_current ();
-
- g_clear_object (&self->gl_context);
-}
-
-static GdkDrawingContext *
-gsk_gl_renderer_begin_draw_frame (GskRenderer *renderer,
- const cairo_region_t *update_area)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
- cairo_region_t *damage;
- GdkDrawingContext *result;
- GdkRectangle whole_window;
- GdkWindow *window;
-
- window = gsk_renderer_get_window (renderer);
- whole_window = (GdkRectangle) {
- 0, 0,
- gdk_window_get_width (window) * self->scale_factor,
- gdk_window_get_height (window) * self->scale_factor
- };
- damage = gdk_gl_context_get_damage (self->gl_context);
- cairo_region_union (damage, update_area);
-
- if (cairo_region_contains_rectangle (damage, &whole_window) == CAIRO_REGION_OVERLAP_IN)
- {
- self->render_mode = RENDER_FULL;
- }
- else
- {
- GdkRectangle extents;
-
- cairo_region_get_extents (damage, &extents);
- cairo_region_union_rectangle (damage, &extents);
-
- if (gdk_rectangle_equal (&extents, &whole_window))
- self->render_mode = RENDER_FULL;
- else
- self->render_mode = RENDER_SCISSOR;
- }
-
- result = gdk_window_begin_draw_frame (window,
- GDK_DRAW_CONTEXT (self->gl_context),
- damage);
-
- cairo_region_destroy (damage);
-
- return result;
-}
-
-static void
-gsk_gl_renderer_resize_viewport (GskGLRenderer *self,
- const graphene_rect_t *viewport)
-{
- int width = viewport->size.width;
- int height = viewport->size.height;
-
- GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %d, %d) [scale:%d]\n",
- width,
- height,
- self->scale_factor));
-
- graphene_rect_init (&self->viewport, 0, 0, width, height);
- glViewport (0, 0, width, height);
-}
-
-
-static void
-get_gl_scaling_filters (GskRenderNode *node,
- int *min_filter_r,
- int *mag_filter_r)
-{
- *min_filter_r = GL_NEAREST;
- *mag_filter_r = GL_NEAREST;
-}
-
-static void
-gsk_gl_renderer_clear_tree (GskGLRenderer *self)
-{
- int removed_textures, removed_vaos;
-
- if (self->gl_context == NULL)
- return;
-
- gdk_gl_context_make_current (self->gl_context);
-
- g_array_remove_range (self->render_ops, 0, self->render_ops->len);
-
- removed_textures = gsk_gl_driver_collect_textures (self->gl_driver);
- removed_vaos = gsk_gl_driver_collect_vaos (self->gl_driver);
-
- GSK_NOTE (OPENGL, g_print ("Collected: %d textures, %d vaos\n",
- removed_textures,
- removed_vaos));
-}
-
-static void
-gsk_gl_renderer_clear (GskGLRenderer *self)
-{
- GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
- glClearColor (0, 0, 0, 0);
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-}
-
-static void
-gsk_gl_renderer_setup_render_mode (GskGLRenderer *self)
-{
- switch (self->render_mode)
- {
- case RENDER_FULL:
- glDisable (GL_SCISSOR_TEST);
- break;
-
- case RENDER_SCISSOR:
- {
- GdkDrawingContext *context = gsk_renderer_get_drawing_context (GSK_RENDERER (self));
- GdkWindow *window = gsk_renderer_get_window (GSK_RENDERER (self));
- cairo_region_t *clip = gdk_drawing_context_get_clip (context);
- cairo_rectangle_int_t extents;
- int window_height;
-
- /* Fall back to RENDER_FULL */
- if (clip == NULL)
- {
- glDisable (GL_SCISSOR_TEST);
- return;
- }
-
- g_assert (cairo_region_num_rectangles (clip) == 1);
-
- window_height = gdk_window_get_height (window) * self->scale_factor;
-
- /*cairo_region_get_extents (clip, &extents);*/
- cairo_region_get_rectangle (clip, 0, &extents);
-
- glEnable (GL_SCISSOR_TEST);
- glScissor (extents.x * self->scale_factor,
- window_height - (extents.height * self->scale_factor) - (extents.y * self->scale_factor),
- extents.width * self->scale_factor,
- extents.height * self->scale_factor);
-
- cairo_region_destroy (clip);
- break;
- }
-
- default:
- g_assert_not_reached ();
- break;
- }
-}
-
-
-static void
-gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
- GskRenderNode *node,
- RenderOpBuilder *builder)
-{
- float min_x = node->bounds.origin.x;
- float min_y = node->bounds.origin.y;
- float max_x = min_x + node->bounds.size.width;
- float max_y = min_y + node->bounds.size.height;
-
- /* Default vertex data */
- GskQuadVertex vertex_data[N_VERTICES] = {
- { { min_x, min_y }, { 0, 0 }, },
- { { min_x, max_y }, { 0, 1 }, },
- { { max_x, min_y }, { 1, 0 }, },
-
- { { max_x, max_y }, { 1, 1 }, },
- { { min_x, max_y }, { 0, 1 }, },
- { { max_x, min_y }, { 1, 0 }, },
- };
-
- /*if (gsk_render_node_get_node_type (node) != GSK_CONTAINER_NODE)*/
- /*g_message ("Adding ops for node %s with type %u", node->name,*/
- /*gsk_render_node_get_node_type (node));*/
-
-
- switch (gsk_render_node_get_node_type (node))
- {
- case GSK_NOT_A_RENDER_NODE:
- g_assert_not_reached ();
-
- case GSK_CONTAINER_NODE:
- {
- guint i, p;
-
- for (i = 0, p = gsk_container_node_get_n_children (node); i < p; i ++)
- {
- GskRenderNode *child = gsk_container_node_get_child (node, i);
-
- gsk_gl_renderer_add_render_ops (self, child, builder);
- }
- }
- break;
-
- case GSK_COLOR_NODE:
- {
- RenderOp op;
-
- add_program_op (builder, &self->color_program);
- op.op = OP_CHANGE_COLOR;
- op.color = *gsk_color_node_peek_color (node);
- add_op (builder, &op);
- add_draw_op (builder, vertex_data);
- }
- break;
-
- case GSK_TEXTURE_NODE:
- {
- GdkTexture *texture = gsk_texture_node_get_texture (node);
- int gl_min_filter = GL_NEAREST, gl_mag_filter = GL_NEAREST;
- int texture_id;
-
- get_gl_scaling_filters (node, &gl_min_filter, &gl_mag_filter);
-
- texture_id = gsk_gl_driver_get_texture_for_texture (self->gl_driver,
- texture,
- gl_min_filter,
- gl_mag_filter);
- add_program_op (builder, &self->blit_program);
- add_texture_op (builder, texture_id);
- add_draw_op (builder, vertex_data);
- }
- break;
-
- case GSK_TRANSFORM_NODE:
- {
- GskRenderNode *child = gsk_transform_node_get_child (node);
- graphene_matrix_t prev_mv;
- graphene_matrix_t transform, transformed_mv;
-
- graphene_matrix_init_from_matrix (&transform, gsk_transform_node_peek_transform (node));
- graphene_matrix_multiply (&transform, &builder->current_modelview, &transformed_mv);
- prev_mv = add_modelview_op (builder, &transformed_mv);
-
- gsk_gl_renderer_add_render_ops (self, child, builder);
-
- add_modelview_op (builder, &prev_mv);
- }
- break;
-
- case GSK_OPACITY_NODE:
- {
- int render_target;
- int texture;
- int prev_render_target;
- float prev_opacity;
- graphene_matrix_t identity;
- graphene_matrix_t prev_projection;
- graphene_matrix_t prev_modelview;
- graphene_rect_t prev_viewport;
- graphene_matrix_t item_proj;
- GskQuadVertex vertex_data[N_VERTICES] = {
- { { min_x, min_y }, { 0, 1 }, },
- { { min_x, max_y }, { 0, 0 }, },
- { { max_x, min_y }, { 1, 1 }, },
-
- { { max_x, max_y }, { 1, 0 }, },
- { { min_x, max_y }, { 0, 0 }, },
- { { max_x, min_y }, { 1, 1 }, },
- };
-
- texture = gsk_gl_driver_create_texture (self->gl_driver,
- node->bounds.size.width,
- node->bounds.size.height);
- gsk_gl_driver_bind_source_texture (self->gl_driver, texture);
- gsk_gl_driver_init_texture_empty (self->gl_driver, texture);
- render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture, TRUE, TRUE);
-
- graphene_matrix_init_ortho (&item_proj,
- min_x, max_x,
- min_y, max_y,
- ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
- graphene_matrix_scale (&item_proj, 1, -1, 1);
- graphene_matrix_init_identity (&identity);
-
- prev_render_target = add_render_target_op (builder, render_target);
- prev_projection = add_projection_op (builder, &item_proj);
- prev_modelview = add_modelview_op (builder, &identity);
- prev_viewport = add_viewport_op (builder, &node->bounds);
-
- gsk_gl_renderer_add_render_ops (self, gsk_opacity_node_get_child (node), builder);
-
- add_viewport_op (builder, &prev_viewport);
- add_modelview_op (builder, &prev_modelview);
- add_projection_op (builder, &prev_projection);
- add_render_target_op (builder, prev_render_target);
-
- add_program_op (builder, &self->blit_program);
- prev_opacity = add_opacity_op (builder, gsk_opacity_node_get_opacity (node));
- add_texture_op (builder, texture);
- add_draw_op (builder, vertex_data);
- add_opacity_op (builder, prev_opacity);
- }
- break;
-
- case GSK_LINEAR_GRADIENT_NODE:
- {
- RenderOp op;
- int n_color_stops = MIN (8, gsk_linear_gradient_node_get_n_color_stops (node));
- const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
- const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
- const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node);
- int i;
-
- for (i = 0; i < n_color_stops; i ++)
- {
- const GskColorStop *stop = stops + i;
-
- op.linear_gradient.color_stops[(i * 4) + 0] = stop->color.red;
- op.linear_gradient.color_stops[(i * 4) + 1] = stop->color.green;
- op.linear_gradient.color_stops[(i * 4) + 2] = stop->color.blue;
- op.linear_gradient.color_stops[(i * 4) + 3] = stop->color.alpha;
- op.linear_gradient.color_offsets[i] = stop->offset;
- }
-
- add_program_op (builder, &self->linear_gradient_program);
- op.op = OP_CHANGE_LINEAR_GRADIENT;
- op.linear_gradient.n_color_stops = n_color_stops;
- op.linear_gradient.start_point = *start;
- op.linear_gradient.end_point = *end;
- add_op (builder, &op);
-
- add_draw_op (builder, vertex_data);
- }
- break;
-
- case GSK_CLIP_NODE:
- {
- GskRoundedRect prev_clip;
- GskRenderNode *child = gsk_clip_node_get_child (node);
- graphene_rect_t transformed_clip;
- graphene_rect_t intersection;
- GskRoundedRect child_clip;
-
- transformed_clip = *gsk_clip_node_peek_clip (node);
- graphene_matrix_transform_bounds (&builder->current_modelview, &transformed_clip, &transformed_clip);
-
- graphene_rect_intersection (&transformed_clip,
- &builder->current_clip.bounds,
- &intersection);
-
- gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f);
-
- prev_clip = add_clip_op (builder, &child_clip);
- gsk_gl_renderer_add_render_ops (self, child, builder);
- add_clip_op (builder, &prev_clip);
- }
- break;
-
- case GSK_ROUNDED_CLIP_NODE:
- {
- GskRoundedRect prev_clip;
- GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
- const GskRoundedRect *rounded_clip = gsk_rounded_clip_node_peek_clip (node);
- graphene_rect_t transformed_clip;
- graphene_rect_t intersection;
- GskRoundedRect child_clip;
-
- transformed_clip = rounded_clip->bounds;
- graphene_matrix_transform_bounds (&builder->current_modelview, &transformed_clip, &transformed_clip);
-
- graphene_rect_intersection (&transformed_clip, &builder->current_clip.bounds,
- &intersection);
- gsk_rounded_rect_init (&child_clip, &intersection,
- &rounded_clip->corner[0],
- &rounded_clip->corner[1],
- &rounded_clip->corner[2],
- &rounded_clip->corner[3]);
-
- prev_clip = add_clip_op (builder, &child_clip);
- gsk_gl_renderer_add_render_ops (self, child, builder);
- add_clip_op (builder, &prev_clip);
- }
- break;
-
- case GSK_TEXT_NODE:
- {
- const PangoFont *font = gsk_text_node_peek_font (node);
- const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
- guint num_glyphs = gsk_text_node_get_num_glyphs (node);
- int i;
- int x_position = 0;
- int x = gsk_text_node_get_x (node);
- int y = gsk_text_node_get_y (node);
-
- /* We use one quad per character, unlike the other nodes which
- * use at most one quad altogether */
- for (i = 0; i < num_glyphs; i++)
- {
- const PangoGlyphInfo *gi = &glyphs[i];
- const GskGLCachedGlyph *glyph;
- int glyph_x, glyph_y, glyph_w, glyph_h;
- float tx, ty, tx2, ty2;
- double cx;
- double cy;
-
- if (gi->glyph == PANGO_GLYPH_EMPTY ||
- (gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG) > 0)
- continue;
-
- glyph = gsk_gl_glyph_cache_lookup (&self->glyph_cache,
- TRUE,
- (PangoFont *)font,
- gi->glyph,
- self->scale_factor);
-
- /* e.g. whitespace */
- if (glyph->draw_width <= 0 || glyph->draw_height <= 0)
- {
- x_position += gi->geometry.width;
- continue;
- }
- cx = (double)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
- cy = (double)(gi->geometry.y_offset) / PANGO_SCALE;
-
- /* If the font has color glyphs, we don't need to recolor anything */
- if (font_has_color_glyphs (font))
- {
- add_program_op (builder, &self->blit_program);
- }
- else
- {
- RenderOp op;
-
- add_program_op (builder, &self->coloring_program);
-
- op.op = OP_CHANGE_COLOR;
- op.color = *gsk_text_node_peek_color (node);
- add_op (builder, &op);
- }
-
- add_texture_op (builder, gsk_gl_glyph_cache_get_glyph_image (&self->glyph_cache,
- glyph)->texture_id);
-
- {
- tx = glyph->tx;
- ty = glyph->ty;
- tx2 = tx + glyph->tw;
- ty2 = ty + glyph->th;
-
- glyph_x = x + cx + glyph->draw_x;
- glyph_y = y + cy + glyph->draw_y;
- glyph_w = glyph->draw_width;
- glyph_h = glyph->draw_height;
-
- GskQuadVertex vertex_data[N_VERTICES] = {
- { { glyph_x, glyph_y }, { tx, ty }, },
- { { glyph_x, glyph_y + glyph_h }, { tx, ty2 }, },
- { { glyph_x + glyph_w, glyph_y }, { tx2, ty }, },
-
- { { glyph_x + glyph_w, glyph_y + glyph_h }, { tx2, ty2 }, },
- { { glyph_x, glyph_y + glyph_h }, { tx, ty2 }, },
- { { glyph_x + glyph_w, glyph_y }, { tx2, ty }, },
- };
-
- add_draw_op (builder, vertex_data);
- }
-
- x_position += gi->geometry.width;
- }
-
- }
- break;
-
-
- case GSK_REPEATING_LINEAR_GRADIENT_NODE:
- case GSK_BORDER_NODE:
- case GSK_INSET_SHADOW_NODE:
- case GSK_OUTSET_SHADOW_NODE:
- case GSK_BLUR_NODE:
- case GSK_SHADOW_NODE:
- case GSK_CROSS_FADE_NODE:
- case GSK_BLEND_NODE:
- case GSK_REPEAT_NODE:
- case GSK_CAIRO_NODE:
- case GSK_COLOR_MATRIX_NODE:
- default:
- {
- cairo_surface_t *surface;
- cairo_t *cr;
- int texture_id;
-
- surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
- ceilf (node->bounds.size.width) * self->scale_factor,
- ceilf (node->bounds.size.height) * self->scale_factor);
- cairo_surface_set_device_scale (surface, self->scale_factor, self->scale_factor);
- cr = cairo_create (surface);
-
- cairo_save (cr);
- cairo_translate (cr, -min_x, -min_y);
- gsk_render_node_draw (node, cr);
- cairo_restore (cr);
-
-#if HIGHLIGHT_FALLBACK
- cairo_move_to (cr, 0, 0);
- cairo_rectangle (cr, 0, 0, max_x - min_x, max_y - min_y);
- cairo_set_source_rgba (cr, 1, 0, 0, 1);
- cairo_stroke (cr);
-#endif
- cairo_destroy (cr);
-
- /* Upload the Cairo surface to a GL texture */
- texture_id = gsk_gl_driver_create_texture (self->gl_driver,
- node->bounds.size.width * self->scale_factor,
- node->bounds.size.height * self->scale_factor);
-
- gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
- gsk_gl_driver_init_texture_with_surface (self->gl_driver,
- texture_id,
- surface,
- GL_NEAREST, GL_NEAREST);
-
- cairo_surface_destroy (surface);
-
- add_program_op (builder, &self->blit_program);
- add_texture_op (builder, texture_id);
- add_draw_op (builder, vertex_data);
- }
- }
-}
-
-static void
-gsk_gl_renderer_render_ops (GskGLRenderer *self,
- gsize vertex_data_size)
-{
- guint i;
- guint n_ops = self->render_ops->len;
- float mat[16];
- const Program *program = NULL;
- gsize buffer_index = 0;
- float *vertex_data = g_malloc (vertex_data_size);
-
- /*g_message ("%s: Buffer size: %ld", __FUNCTION__, vertex_data_size);*/
-
-
- GLuint buffer_id, vao_id;
- glGenVertexArrays (1, &vao_id);
- glBindVertexArray (vao_id);
-
- glGenBuffers (1, &buffer_id);
- glBindBuffer (GL_ARRAY_BUFFER, buffer_id);
-
-
- // Fill buffer data
- for (i = 0; i < n_ops; i ++)
- {
- const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
-
- if (op->op == OP_CHANGE_VAO)
- {
- memcpy (vertex_data + buffer_index, &op->vertex_data, sizeof (GskQuadVertex) * N_VERTICES);
- buffer_index += sizeof (GskQuadVertex) * N_VERTICES / sizeof (float);
- }
- }
-
- // Set buffer data
- glBufferData (GL_ARRAY_BUFFER, vertex_data_size, vertex_data, GL_STATIC_DRAW);
-
- // Describe buffer contents
-
- /* 0 = position location */
- glEnableVertexAttribArray (0);
- glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE,
- sizeof (GskQuadVertex),
- (void *) G_STRUCT_OFFSET (GskQuadVertex, position));
- /* 1 = texture coord location */
- glEnableVertexAttribArray (1);
- glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE,
- sizeof (GskQuadVertex),
- (void *) G_STRUCT_OFFSET (GskQuadVertex, uv));
-
- for (i = 0; i < n_ops; i ++)
- {
- const RenderOp *op = &g_array_index (self->render_ops, RenderOp, i);
-
- if (op->op == OP_NONE ||
- op->op == OP_CHANGE_VAO)
- continue;
-
- OP_PRINT ("Op %u: %u", i, op->op);
-
- switch (op->op)
- {
- case OP_CHANGE_PROJECTION:
- graphene_matrix_to_float (&op->projection, mat);
- glUniformMatrix4fv (program->projection_location, 1, GL_FALSE, mat);
- OP_PRINT (" -> Projection");
- /*graphene_matrix_print (&op->projection);*/
- break;
-
- case OP_CHANGE_MODELVIEW:
- graphene_matrix_to_float (&op->modelview, mat);
- glUniformMatrix4fv (program->modelview_location, 1, GL_FALSE, mat);
- OP_PRINT (" -> Modelview");
- /*graphene_matrix_print (&op->modelview);*/
- break;
-
- case OP_CHANGE_PROGRAM:
- program = op->program;
- glUseProgram (op->program->id);
- OP_PRINT (" -> Program: %d", op->program->id);
- break;
-
- case OP_CHANGE_RENDER_TARGET:
- OP_PRINT (" -> Render Target: %d", op->render_target_id);
-
- glBindFramebuffer (GL_FRAMEBUFFER, op->render_target_id);
- if (op->render_target_id != 0)
- {
- glDisable (GL_SCISSOR_TEST);
- glClearColor (0.0, 0.0, 0.0, 0.0);
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
- }
- else
- {
- /* Reset glScissor, etc. */
- gsk_gl_renderer_setup_render_mode (self);
- }
- break;
-
- case OP_CHANGE_VIEWPORT:
- OP_PRINT (" -> New Viewport: %f, %f, %f, %f", op->viewport.origin.x, op->viewport.origin.y, op->viewport.size.width, op->viewport.size.height);
- glUniform4f (program->viewport_location,
- op->viewport.origin.x, op->viewport.origin.y,
- op->viewport.size.width, op->viewport.size.height);
- glViewport (0, 0, op->viewport.size.width, op->viewport.size.height);
- break;
-
- case OP_CHANGE_OPACITY:
- glUniform1f (program->alpha_location, op->opacity);
- break;
-
- case OP_CHANGE_COLOR:
- OP_PRINT (" -> Color: (%f, %f, %f, %f)", op->color.red, op->color.green, op->color.blue, op->color.alpha);
- g_assert (program == &self->color_program || program == &self->coloring_program);
- glUniform4f (program->color_location,
- op->color.red, op->color.green, op->color.blue, op->color.alpha);
- break;
-
- case OP_CHANGE_CLIP:
- OP_PRINT (" -> Clip");
- glUniform4f (program->clip_location,
- op->clip.bounds.origin.x, op->clip.bounds.origin.y,
- op->clip.bounds.size.width, op->clip.bounds.size.height);
-
- glUniform4f (program->clip_corner_widths_location,
- MAX (op->clip.corner[0].width, 1),
- MAX (op->clip.corner[1].width, 1),
- MAX (op->clip.corner[2].width, 1),
- MAX (op->clip.corner[3].width, 1));
- glUniform4f (program->clip_corner_heights_location,
- MAX (op->clip.corner[0].height, 1),
- MAX (op->clip.corner[1].height, 1),
- MAX (op->clip.corner[2].height, 1),
- MAX (op->clip.corner[3].height, 1));
- break;
-
- case OP_CHANGE_SOURCE_TEXTURE:
- g_assert(op->texture_id != 0);
- OP_PRINT (" -> New texture: %d", op->texture_id);
- /* Use texture unit 0 for the source */
- glUniform1i (program->source_location, 0);
- glActiveTexture (GL_TEXTURE0);
- glBindTexture (GL_TEXTURE_2D, op->texture_id);
-
- break;
-
- case OP_CHANGE_LINEAR_GRADIENT:
- OP_PRINT (" -> Linear gradient");
- glUniform1i (program->n_color_stops_location,
- op->linear_gradient.n_color_stops);
- glUniform4fv (program->color_stops_location,
- op->linear_gradient.n_color_stops,
- op->linear_gradient.color_stops);
- glUniform1fv (program->color_offsets_location,
- op->linear_gradient.n_color_stops,
- op->linear_gradient.color_offsets);
- glUniform2f (program->start_point_location,
- op->linear_gradient.start_point.x, op->linear_gradient.start_point.y);
- glUniform2f (program->end_point_location,
- op->linear_gradient.end_point.x, op->linear_gradient.end_point.y);
- break;
-
- case OP_DRAW:
- OP_PRINT (" -> draw %ld\n", op->vao_offset);
- glDrawArrays (GL_TRIANGLES, op->vao_offset, N_VERTICES);
- break;
-
- default:
- g_warn_if_reached ();
- }
-
- OP_PRINT ("\n");
- }
-
- /* Done drawing, destroy the buffer again.
- * TODO: Can we reuse the memory, though? */
- g_free (vertex_data);
-}
-
-static void
-gsk_gl_renderer_do_render (GskRenderer *renderer,
- GskRenderNode *root,
- const graphene_rect_t *viewport,
- int scale_factor)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
- RenderOpBuilder render_op_builder;
- graphene_matrix_t modelview, projection;
-#ifdef G_ENABLE_DEBUG
- GskProfiler *profiler;
- gint64 gpu_time, cpu_time;
-#endif
-
-#ifdef G_ENABLE_DEBUG
- profiler = gsk_renderer_get_profiler (renderer);
-#endif
-
- if (self->gl_context == NULL)
- {
- GSK_NOTE (OPENGL, g_print ("No valid GL context associated to the renderer"));
- return;
- }
-
- self->viewport = *viewport;
-
- /* Set up the modelview and projection matrices to fit our viewport */
- graphene_matrix_init_scale (&modelview, scale_factor, scale_factor, 1.0);
- graphene_matrix_init_ortho (&projection,
- viewport->origin.x,
- viewport->origin.x + viewport->size.width,
- viewport->origin.y,
- viewport->origin.y + viewport->size.height,
- ORTHO_NEAR_PLANE,
- ORTHO_FAR_PLANE);
-
- if (self->texture_id == 0)
- graphene_matrix_scale (&projection, 1, -1, 1);
-
- gsk_gl_driver_begin_frame (self->gl_driver);
- gsk_gl_glyph_cache_begin_frame (&self->glyph_cache);
-
- memset (&render_op_builder, 0, sizeof (render_op_builder));
- render_op_builder.renderer = self;
- render_op_builder.current_projection = projection;
- render_op_builder.current_modelview = modelview;
- render_op_builder.current_viewport = *viewport;
- render_op_builder.current_render_target = self->texture_id;
- render_op_builder.current_opacity = 1.0f;
- gsk_rounded_rect_init_from_rect (&render_op_builder.current_clip, &self->viewport, 0.0f);
- gsk_gl_renderer_add_render_ops (self, root, &render_op_builder);
-
- /*g_message ("Ops: %u", self->render_ops->len);*/
-
- /* Now actually draw things... */
-#ifdef G_ENABLE_DEBUG
- gsk_gl_profiler_begin_gpu_region (self->gl_profiler);
- gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
-#endif
-
- gsk_gl_renderer_resize_viewport (self, viewport);
- gsk_gl_renderer_setup_render_mode (self);
- gsk_gl_renderer_clear (self);
-
- glEnable (GL_DEPTH_TEST);
- glDepthFunc (GL_LEQUAL);
-
- /* Pre-multiplied alpha! */
- glEnable (GL_BLEND);
- glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glBlendEquation (GL_FUNC_ADD);
-
- gsk_gl_renderer_render_ops (self, render_op_builder.buffer_size);
-
- gsk_gl_driver_end_frame (self->gl_driver);
-
-#ifdef G_ENABLE_DEBUG
- gsk_profiler_counter_inc (profiler, self->profile_counters.frames);
-
- cpu_time = gsk_profiler_timer_end (profiler, self->profile_timers.cpu_time);
- gsk_profiler_timer_set (profiler, self->profile_timers.cpu_time, cpu_time);
-
- gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler);
- gsk_profiler_timer_set (profiler, self->profile_timers.gpu_time, gpu_time);
-
- gsk_profiler_push_samples (profiler);
-#endif
-}
-
-static GdkTexture *
-gsk_gl_renderer_render_texture (GskRenderer *renderer,
- GskRenderNode *root,
- const graphene_rect_t *viewport)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
- GdkTexture *texture;
- int stride;
- guchar *data;
- int width, height;
-
- g_return_val_if_fail (self->gl_context != NULL, NULL);
-
- self->render_mode = RENDER_FULL;
- width = ceilf (viewport->size.width);
- height = ceilf (viewport->size.height);
-
- gdk_gl_context_make_current (self->gl_context);
-
- /* Prepare our framebuffer */
- gsk_gl_driver_begin_frame (self->gl_driver);
- gsk_gl_renderer_create_buffers (self, width, height, 1);
- gsk_gl_renderer_clear (self);
- gsk_gl_driver_end_frame (self->gl_driver);
-
- /* Render the actual scene */
- gsk_gl_renderer_do_render (renderer, root, viewport, 1);
-
- /* Prepare memory for the glReadPixels call */
- stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
- data = g_malloc (height * stride);
-
- /* Bind our framebuffer again and read from it */
- gsk_gl_driver_begin_frame (self->gl_driver);
- gsk_gl_driver_bind_render_target (self->gl_driver, self->texture_id);
- glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
- gsk_gl_driver_end_frame (self->gl_driver);
-
- /* Create texture from the downloaded data */
- texture = gdk_texture_new_for_data (data, width, height, stride);
-
- return texture;
-}
-
-static void
-gsk_gl_renderer_render (GskRenderer *renderer,
- GskRenderNode *root)
-{
- GskGLRenderer *self = GSK_GL_RENDERER (renderer);
- GdkWindow *window = gsk_renderer_get_window (renderer);
- graphene_rect_t viewport;
-
- if (self->gl_context == NULL)
- return;
-
- gdk_gl_context_make_current (self->gl_context);
-
- viewport.origin.x = 0;
- viewport.origin.y = 0;
- viewport.size.width = gdk_window_get_width (window) * self->scale_factor;
- viewport.size.height = gdk_window_get_height (window) * self->scale_factor;
-
- gsk_gl_renderer_do_render (renderer, root, &viewport, self->scale_factor);
-
- gdk_gl_context_make_current (self->gl_context);
- gsk_gl_renderer_clear_tree (self);
- gsk_gl_renderer_destroy_buffers (self);
-}
-
-static void
-gsk_gl_renderer_class_init (GskGLRendererClass *klass)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
-
- gobject_class->dispose = gsk_gl_renderer_dispose;
-
- renderer_class->realize = gsk_gl_renderer_realize;
- renderer_class->unrealize = gsk_gl_renderer_unrealize;
- renderer_class->begin_draw_frame = gsk_gl_renderer_begin_draw_frame;
- renderer_class->render = gsk_gl_renderer_render;
- renderer_class->render_texture = gsk_gl_renderer_render_texture;
-}
-
-static void
-gsk_gl_renderer_init (GskGLRenderer *self)
-{
- gsk_ensure_resources ();
-
-
- self->scale_factor = 1;
- self->render_ops = g_array_new (TRUE, FALSE, sizeof (RenderOp));
-
-#ifdef G_ENABLE_DEBUG
- {
- GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
-
- self->profile_counters.frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE);
- self->profile_counters.draw_calls = gsk_profiler_add_counter (profiler, "draws", "glDrawArrays", TRUE);
-
- self->profile_timers.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU time", FALSE, TRUE);
- self->profile_timers.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU time", FALSE, TRUE);
- }
-#endif
-}
+++ /dev/null
-#ifndef __GSK_GL_RENDERER_PRIVATE_H__
-#define __GSK_GL_RENDERER_PRIVATE_H__
-
-#include <gsk/gskrenderer.h>
-
-G_BEGIN_DECLS
-
-#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ())
-
-#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer))
-#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER))
-#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
-#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER))
-#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
-
-typedef struct _GskGLRenderer GskGLRenderer;
-typedef struct _GskGLRendererClass GskGLRendererClass;
-
-GType gsk_gl_renderer_get_type (void) G_GNUC_CONST;
-
-G_END_DECLS
-
-#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */
#include "gskcairorendererprivate.h"
#include "gskdebugprivate.h"
-#include "gskglrendererprivate.h"
+#include "gl/gskglrendererprivate.h"
#include "gskprofilerprivate.h"
#include "gskrendernodeprivate.h"
'gskcairoblur.c',
'gskcairorenderer.c',
'gskdebug.c',
- 'gskgldriver.c',
- 'gskglprofiler.c',
- 'gskglrenderer.c',
- 'gskglglyphcache.c',
- 'gskglimage.c',
'gskprivate.c',
'gskprofiler.c',
'gskshaderbuilder.c',
+ 'gl/gskglprofiler.c',
+ 'gl/gskglrenderer.c',
+ 'gl/gskglglyphcache.c',
+ 'gl/gskglimage.c',
+ 'gl/gskgldriver.c',
])
gsk_public_headers = files([